| @@ -1,11 +1,11 @@ | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>1.3.5</version> | |||
| <version>2.0.0-SNAPSHOT</version> | |||
| <packaging>pom</packaging> | |||
| <name>WeiXin Java Tools - Parent</name> | |||
| <description>微信公众号、企业号上级POM</description> | |||
| @@ -39,7 +39,6 @@ | |||
| <module>weixin-java-common</module> | |||
| <module>weixin-java-cp</module> | |||
| <module>weixin-java-mp</module> | |||
| <module>spring-demo</module> | |||
| </modules> | |||
| <properties> | |||
| @@ -50,6 +49,13 @@ | |||
| <slf4j.version>1.7.10</slf4j.version> | |||
| <logback.version>1.1.2</logback.version> | |||
| <jodd-http.version>3.6.7</jodd-http.version> | |||
| <jackson.version>2.8.0</jackson.version> | |||
| <gson.version>2.7</gson.version> | |||
| <commons-lang3.version>3.4</commons-lang3.version> | |||
| <commons-io.version>2.5</commons-io.version> | |||
| <commons-codec.version>1.10</commons-codec.version> | |||
| <jetty.version>9.3.0.M0</jetty.version> | |||
| <jetty-new.version>9.3.10.v20160621</jetty-new.version> | |||
| </properties> | |||
| <dependencies> | |||
| @@ -82,20 +88,30 @@ | |||
| <dependency> | |||
| <groupId>com.google.code.gson</groupId> | |||
| <artifactId>gson</artifactId> | |||
| <version>2.2.2</version> | |||
| <version>${gson.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-codec</groupId> | |||
| <artifactId>commons-codec</artifactId> | |||
| <version>1.9</version> | |||
| <version>${commons-codec.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>commons-io</groupId> | |||
| <artifactId>commons-io</artifactId> | |||
| <version>2.4</version> | |||
| <version>${commons-io.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| <version>${commons-lang3.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.fasterxml.jackson.core</groupId> | |||
| <artifactId>jackson-databind</artifactId> | |||
| <version>${jackson.version}</version> | |||
| </dependency> | |||
| </dependencies> | |||
| <dependencyManagement> | |||
| <dependencies> | |||
| <dependency> | |||
| @@ -125,13 +141,13 @@ | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-server</artifactId> | |||
| <version>9.3.0.M0</version> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <version>9.3.0.M0</version> | |||
| <version>${jetty.version}</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| @@ -149,16 +165,16 @@ | |||
| </distributionManagement> | |||
| <profiles> | |||
| <profile> | |||
| <id>doclint-java8-disable</id> | |||
| <activation> | |||
| <jdk>[1.8,)</jdk> | |||
| </activation> | |||
| <properties> | |||
| <javadoc.opts>-Xdoclint:none</javadoc.opts> | |||
| </properties> | |||
| </profile> | |||
| <profile> | |||
| <id>doclint-java8-disable</id> | |||
| <activation> | |||
| <jdk>[1.8,)</jdk> | |||
| </activation> | |||
| <properties> | |||
| <javadoc.opts>-Xdoclint:none</javadoc.opts> | |||
| </properties> | |||
| </profile> | |||
| <profile> | |||
| <id>release</id> | |||
| <build> | |||
| @@ -220,6 +236,9 @@ | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-surefire-plugin</artifactId> | |||
| <version>2.17</version> | |||
| <configuration> | |||
| <skip>true</skip> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </pluginManagement> | |||
| @@ -1,25 +0,0 @@ | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>1.3.5</version> | |||
| </parent> | |||
| <artifactId>spring-demo</artifactId> | |||
| <packaging>war</packaging> | |||
| <name>WeiXin Java Tools - demo with spring</name> | |||
| <description>spring demo</description> | |||
| <url>https://github.com/binarywang/weixin-java-tools</url> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -6,7 +6,7 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>1.3.5</version> | |||
| <version>2.0.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>weixin-java-common</artifactId> | |||
| @@ -11,6 +11,7 @@ public class WxConsts { | |||
| public static final String XML_MSG_TEXT = "text"; | |||
| public static final String XML_MSG_IMAGE = "image"; | |||
| public static final String XML_MSG_VOICE = "voice"; | |||
| public static final String XML_MSG_SHORTVIDEO = "shortvideo"; | |||
| public static final String XML_MSG_VIDEO = "video"; | |||
| public static final String XML_MSG_NEWS = "news"; | |||
| public static final String XML_MSG_MUSIC = "music"; | |||
| @@ -20,7 +21,7 @@ public class WxConsts { | |||
| public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
| /////////////////////// | |||
| // 主动发送消息的消息类型 | |||
| // 主动发送消息(即客服消息)的消息类型 | |||
| /////////////////////// | |||
| public static final String CUSTOM_MSG_TEXT = "text"; | |||
| public static final String CUSTOM_MSG_IMAGE = "image"; | |||
| @@ -103,6 +104,10 @@ public class WxConsts { | |||
| public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; | |||
| public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 | |||
| public static final String EVT_KF_CREATE_SESSION = "kf_create_session"; // 客服接入会话 | |||
| public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | |||
| public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | |||
| /////////////////////// | |||
| // 上传多媒体文件的类型 | |||
| /////////////////////// | |||
| @@ -133,13 +138,17 @@ public class WxConsts { | |||
| /** 扫码推事件且弹出“消息接收中”提示框 */ | |||
| public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; | |||
| /** 弹出系统拍照发图 */ | |||
| public static final String PIC_SYSPHOTO = "pic_sysphoto"; | |||
| public static final String BUTTON_PIC_SYSPHOTO = "pic_sysphoto"; | |||
| /** 弹出拍照或者相册发图 */ | |||
| public static final String PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | |||
| public static final String BUTTON_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | |||
| /** 弹出微信相册发图器 */ | |||
| public static final String PIC_WEIXIN = "pic_weixin"; | |||
| public static final String BUTTON_PIC_WEIXIN = "pic_weixin"; | |||
| /** 弹出地理位置选择器 */ | |||
| public static final String LOCATION_SELECT = "location_select"; | |||
| public static final String BUTTON_LOCATION_SELECT = "location_select"; | |||
| /** 下发消息(除文本消息) */ | |||
| public static final String BUTTON_MEDIA_ID = "media_id"; | |||
| /** 跳转图文消息URL */ | |||
| public static final String BUTTON_VIEW_LIMITED = "view_limited"; | |||
| /////////////////////// | |||
| // oauth2网页授权的scope | |||
| @@ -1,14 +1,14 @@ | |||
| package me.chanjar.weixin.common.bean; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import org.apache.commons.codec.Charsets; | |||
| import java.io.InputStream; | |||
| import java.io.InputStreamReader; | |||
| import java.io.Serializable; | |||
| import java.nio.charset.StandardCharsets; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| /** | |||
| * 企业号菜单 | |||
| * @author Daniel Qian | |||
| @@ -45,8 +45,6 @@ public class WxMenu implements Serializable { | |||
| /** | |||
| * 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||
| * 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||
| * @param json | |||
| * @return | |||
| */ | |||
| public static WxMenu fromJson(String json) { | |||
| return WxGsonBuilder.create().fromJson(json, WxMenu.class); | |||
| @@ -55,11 +53,9 @@ public class WxMenu implements Serializable { | |||
| /** | |||
| * 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||
| * 相比 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html 的格式,外层多套了一个menu | |||
| * @param is | |||
| * @return | |||
| */ | |||
| public static WxMenu fromJson(InputStream is) { | |||
| return WxGsonBuilder.create().fromJson(new InputStreamReader(is, Charsets.UTF_8), WxMenu.class); | |||
| return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||
| } | |||
| @Override | |||
| @@ -195,7 +191,7 @@ public class WxMenu implements Serializable { | |||
| this.language = language; | |||
| } | |||
| @Override | |||
| @Override | |||
| public String toString() { | |||
| return "matchrule:{" + | |||
| "tag_id='" + tagId + '\'' + | |||
| @@ -4,16 +4,11 @@ public interface WxSessionManager { | |||
| /** | |||
| * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 | |||
| * @param sessionId | |||
| * @return | |||
| */ | |||
| public WxSession getSession(String sessionId); | |||
| /** | |||
| * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | |||
| * @param sessionId | |||
| * @param create | |||
| * @return | |||
| */ | |||
| public WxSession getSession(String sessionId, boolean create); | |||
| @@ -12,9 +12,6 @@ public class SHA1 { | |||
| /** | |||
| * 串接arr参数,生成sha1 digest | |||
| * | |||
| * @param arr | |||
| * @return | |||
| */ | |||
| public static String gen(String... arr) throws NoSuchAlgorithmException { | |||
| Arrays.sort(arr); | |||
| @@ -27,9 +24,6 @@ public class SHA1 { | |||
| /** | |||
| * 用&串接arr参数,生成sha1 digest | |||
| * | |||
| * @param arr | |||
| * @return | |||
| */ | |||
| public static String genWithAmple(String... arr) throws NoSuchAlgorithmException { | |||
| Arrays.sort(arr); | |||
| @@ -35,16 +35,16 @@ public class WxCryptUtil { | |||
| private static final Base64 base64 = new Base64(); | |||
| private static final Charset CHARSET = Charset.forName("utf-8"); | |||
| private static final ThreadLocal<DocumentBuilder> builderLocal = | |||
| new ThreadLocal<DocumentBuilder>() { | |||
| @Override protected DocumentBuilder initialValue() { | |||
| try { | |||
| return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
| } catch (ParserConfigurationException exc) { | |||
| throw new IllegalArgumentException(exc); | |||
| } | |||
| } | |||
| }; | |||
| private static final ThreadLocal<DocumentBuilder> builderLocal = new ThreadLocal<DocumentBuilder>() { | |||
| @Override | |||
| protected DocumentBuilder initialValue() { | |||
| try { | |||
| return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
| } catch (ParserConfigurationException exc) { | |||
| throw new IllegalArgumentException(exc); | |||
| } | |||
| } | |||
| }; | |||
| protected byte[] aesKey; | |||
| protected String token; | |||
| @@ -61,7 +61,8 @@ public class WxCryptUtil { | |||
| * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||
| * @param appidOrCorpid 公众平台appid/corpid | |||
| */ | |||
| public WxCryptUtil(String token, String encodingAesKey, String appidOrCorpid) { | |||
| public WxCryptUtil(String token, String encodingAesKey, | |||
| String appidOrCorpid) { | |||
| this.token = token; | |||
| this.appidOrCorpid = appidOrCorpid; | |||
| this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
| @@ -105,7 +106,8 @@ public class WxCryptUtil { | |||
| ByteGroup byteCollector = new ByteGroup(); | |||
| byte[] randomStringBytes = randomStr.getBytes(CHARSET); | |||
| byte[] plainTextBytes = plainText.getBytes(CHARSET); | |||
| byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder(plainTextBytes.length); | |||
| byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | |||
| plainTextBytes.length); | |||
| byte[] appIdBytes = appidOrCorpid.getBytes(CHARSET); | |||
| // randomStr + networkBytesOrder + text + appid | |||
| @@ -154,7 +156,8 @@ public class WxCryptUtil { | |||
| * @param encryptedXml 密文,对应POST请求的数据 | |||
| * @return 解密后的原文 | |||
| */ | |||
| public String decrypt(String msgSignature, String timeStamp, String nonce, String encryptedXml) { | |||
| public String decrypt(String msgSignature, String timeStamp, String nonce, | |||
| String encryptedXml) { | |||
| // 密钥,公众账号的app corpSecret | |||
| // 提取密文 | |||
| String cipherText = extractEncryptPart(encryptedXml); | |||
| @@ -186,7 +189,8 @@ public class WxCryptUtil { | |||
| // 设置解密模式为AES的CBC模式 | |||
| Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
| SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | |||
| IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); | |||
| IvParameterSpec iv = new IvParameterSpec( | |||
| Arrays.copyOfRange(aesKey, 0, 16)); | |||
| cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||
| // 使用BASE64对密文进行解码 | |||
| @@ -208,9 +212,10 @@ public class WxCryptUtil { | |||
| int xmlLength = bytesNetworkOrder2Number(networkOrder); | |||
| xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET); | |||
| from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), | |||
| xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | |||
| CHARSET); | |||
| from_appid = new String( | |||
| Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
| } catch (Exception e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| @@ -224,34 +229,32 @@ public class WxCryptUtil { | |||
| } | |||
| /** | |||
| * 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||
| * @param packageParams 原始参数 | |||
| * @param signKey 加密Key(即 商户Key) | |||
| * @param charset 编码 | |||
| * @return 签名字符串 | |||
| */ | |||
| public static String createSign(Map<String, String> packageParams, String signKey) { | |||
| SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||
| sortedMap.putAll(packageParams); | |||
| List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||
| Collections.sort(keys); | |||
| StringBuffer toSign = new StringBuffer(); | |||
| for (String key : keys) { | |||
| String value = packageParams.get(key); | |||
| if (null != value && !"".equals(value) && !"sign".equals(key) | |||
| && !"key".equals(key)) { | |||
| toSign.append(key + "=" + value + "&"); | |||
| } | |||
| } | |||
| toSign.append("key=" + signKey); | |||
| String sign = DigestUtils.md5Hex(toSign.toString()) | |||
| .toUpperCase(); | |||
| return sign; | |||
| /** | |||
| * 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||
| * @param packageParams 原始参数 | |||
| * @param signKey 加密Key(即 商户Key) | |||
| * @return 签名字符串 | |||
| */ | |||
| public static String createSign(Map<String, String> packageParams, | |||
| String signKey) { | |||
| SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||
| sortedMap.putAll(packageParams); | |||
| List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||
| Collections.sort(keys); | |||
| StringBuffer toSign = new StringBuffer(); | |||
| for (String key : keys) { | |||
| String value = packageParams.get(key); | |||
| if (null != value && !"".equals(value) && !"sign".equals(key) | |||
| && !"key".equals(key)) { | |||
| toSign.append(key + "=" + value + "&"); | |||
| } | |||
| } | |||
| toSign.append("key=" + signKey); | |||
| String sign = DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||
| return sign; | |||
| } | |||
| /** | |||
| * 将一个数字转换成生成4个字节的网络字节序bytes数组 | |||
| @@ -283,8 +286,6 @@ public class WxCryptUtil { | |||
| /** | |||
| * 随机生成16位字符串 | |||
| * | |||
| * @return | |||
| */ | |||
| private String genRandomStr() { | |||
| String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||
| @@ -306,14 +307,12 @@ public class WxCryptUtil { | |||
| * @param nonce 随机字符串 | |||
| * @return 生成的xml字符串 | |||
| */ | |||
| private String generateXml(String encrypt, String signature, String timestamp, String nonce) { | |||
| String format = | |||
| "<xml>\n" | |||
| + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | |||
| + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
| + "<TimeStamp>%3$s</TimeStamp>\n" | |||
| + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||
| + "</xml>"; | |||
| private String generateXml(String encrypt, String signature, String timestamp, | |||
| String nonce) { | |||
| String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | |||
| + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
| + "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||
| + "</xml>"; | |||
| return String.format(format, encrypt, signature, timestamp, nonce); | |||
| } | |||
| @@ -14,8 +14,6 @@ public class FileUtils { | |||
| * @param name 文件名 | |||
| * @param ext 扩展名 | |||
| * @param tmpDirFile 临时文件夹目录 | |||
| * @return | |||
| * @throws IOException | |||
| */ | |||
| public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException { | |||
| FileOutputStream fos = null; | |||
| @@ -56,8 +54,6 @@ public class FileUtils { | |||
| * @param inputStream | |||
| * @param name 文件名 | |||
| * @param ext 扩展名 | |||
| * @return | |||
| * @throws IOException | |||
| */ | |||
| public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { | |||
| return createTmpFile(inputStream, name, ext, null); | |||
| @@ -17,35 +17,30 @@ public interface ApacheHttpClientBuilder { | |||
| /** | |||
| * 代理服务器地址 | |||
| * @param httpProxyHost | |||
| * @return | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); | |||
| /** | |||
| * 代理服务器端口 | |||
| * @param httpProxyPort | |||
| * @return | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); | |||
| /** | |||
| * 代理服务器用户名 | |||
| * @param httpProxyUsername | |||
| * @return | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); | |||
| /** | |||
| * 代理服务器密码 | |||
| * @param httpProxyPassword | |||
| * @return | |||
| */ | |||
| ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); | |||
| /** | |||
| * ssl连接socket工厂 | |||
| * @param sslConnectionSocketFactory | |||
| * @return | |||
| */ | |||
| ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); | |||
| } | |||
| @@ -22,7 +22,6 @@ public interface RequestExecutor<T, E> { | |||
| * @param httpProxy http代理对象,如果没有配置代理则为空 | |||
| * @param uri uri | |||
| * @param data 数据 | |||
| * @return | |||
| * @throws WxErrorException | |||
| * @throws ClientProtocolException | |||
| * @throws IOException | |||
| @@ -6,7 +6,7 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>1.3.5</version> | |||
| <version>2.0.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>weixin-java-cp</artifactId> | |||
| @@ -9,8 +9,6 @@ public interface WxCpMessageMatcher { | |||
| /** | |||
| * 消息是否匹配某种模式 | |||
| * @param message | |||
| * @return | |||
| */ | |||
| public boolean match(WxCpXmlMessage message); | |||
| @@ -124,7 +124,6 @@ public class WxCpMessageRouter { | |||
| /** | |||
| * 开始一个新的Route规则 | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule rule() { | |||
| return new WxCpMessageRouterRule(this); | |||
| @@ -48,7 +48,6 @@ public class WxCpMessageRouterRule { | |||
| * 设置是否异步执行,默认是true | |||
| * | |||
| * @param async | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule async(boolean async) { | |||
| this.async = async; | |||
| @@ -59,7 +58,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果agentId匹配 | |||
| * | |||
| * @param agentId | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule agentId(Integer agentId) { | |||
| this.agentId = agentId; | |||
| @@ -70,7 +68,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果msgType等于某值 | |||
| * | |||
| * @param msgType | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule msgType(String msgType) { | |||
| this.msgType = msgType; | |||
| @@ -81,7 +78,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果event等于某值 | |||
| * | |||
| * @param event | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule event(String event) { | |||
| this.event = event; | |||
| @@ -92,7 +88,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果eventKey等于某值 | |||
| * | |||
| * @param eventKey | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule eventKey(String eventKey) { | |||
| this.eventKey = eventKey; | |||
| @@ -103,7 +98,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果content等于某值 | |||
| * | |||
| * @param content | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule content(String content) { | |||
| this.content = content; | |||
| @@ -114,7 +108,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果content匹配该正则表达式 | |||
| * | |||
| * @param regex | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule rContent(String regex) { | |||
| this.rContent = regex; | |||
| @@ -125,7 +118,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果fromUser等于某值 | |||
| * | |||
| * @param fromUser | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule fromUser(String fromUser) { | |||
| this.fromUser = fromUser; | |||
| @@ -136,7 +128,6 @@ public class WxCpMessageRouterRule { | |||
| * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候 | |||
| * | |||
| * @param matcher | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule matcher(WxCpMessageMatcher matcher) { | |||
| this.matcher = matcher; | |||
| @@ -147,7 +138,6 @@ public class WxCpMessageRouterRule { | |||
| * 设置微信消息拦截器 | |||
| * | |||
| * @param interceptor | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule interceptor(WxCpMessageInterceptor interceptor) { | |||
| return interceptor(interceptor, (WxCpMessageInterceptor[]) null); | |||
| @@ -158,7 +148,6 @@ public class WxCpMessageRouterRule { | |||
| * | |||
| * @param interceptor | |||
| * @param otherInterceptors | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule interceptor(WxCpMessageInterceptor interceptor, WxCpMessageInterceptor... otherInterceptors) { | |||
| this.interceptors.add(interceptor); | |||
| @@ -174,7 +163,6 @@ public class WxCpMessageRouterRule { | |||
| * 设置微信消息处理器 | |||
| * | |||
| * @param handler | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule handler(WxCpMessageHandler handler) { | |||
| return handler(handler, (WxCpMessageHandler[]) null); | |||
| @@ -185,7 +173,6 @@ public class WxCpMessageRouterRule { | |||
| * | |||
| * @param handler | |||
| * @param otherHandlers | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouterRule handler(WxCpMessageHandler handler, WxCpMessageHandler... otherHandlers) { | |||
| this.handlers.add(handler); | |||
| @@ -200,7 +187,6 @@ public class WxCpMessageRouterRule { | |||
| /** | |||
| * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 | |||
| * | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouter end() { | |||
| this.routerBuilder.getRules().add(this); | |||
| @@ -210,7 +196,6 @@ public class WxCpMessageRouterRule { | |||
| /** | |||
| * 规则结束,但是消息还会进入其他规则 | |||
| * | |||
| * @return | |||
| */ | |||
| public WxCpMessageRouter next() { | |||
| this.reEnter = true; | |||
| @@ -32,7 +32,6 @@ public interface WxCpService { | |||
| * @param timestamp | |||
| * @param nonce | |||
| * @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 | |||
| * @return | |||
| */ | |||
| boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); | |||
| @@ -49,7 +48,6 @@ public interface WxCpService { | |||
| /** | |||
| * 获取access_token, 不强制刷新access_token | |||
| * @see #getAccessToken(boolean) | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| String getAccessToken() throws WxErrorException; | |||
| @@ -63,7 +61,6 @@ public interface WxCpService { | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | |||
| * </pre> | |||
| * @param forceRefresh 强制刷新 | |||
| * @return | |||
| * @throws me.chanjar.weixin.common.exception.WxErrorException | |||
| */ | |||
| String getAccessToken(boolean forceRefresh) throws WxErrorException; | |||
| @@ -71,7 +68,6 @@ public interface WxCpService { | |||
| /** | |||
| * 获得jsapi_ticket,不强制刷新jsapi_ticket | |||
| * @see #getJsapiTicket(boolean) | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| public String getJsapiTicket() throws WxErrorException; | |||
| @@ -84,7 +80,6 @@ public interface WxCpService { | |||
| * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||
| * </pre> | |||
| * @param forceRefresh 强制刷新 | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| public String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | |||
| @@ -96,7 +91,6 @@ public interface WxCpService { | |||
| * 详情请见:http://qydev.weixin.qq.com/wiki/index.php?title=微信JS接口#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 | |||
| * </pre> | |||
| * @param url url | |||
| * @return | |||
| */ | |||
| public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; | |||
| @@ -136,7 +130,7 @@ public interface WxCpService { | |||
| * | |||
| * @return 保存到本地的临时文件 | |||
| * @throws WxErrorException | |||
| * @params media_id | |||
| * @param media_id | |||
| */ | |||
| File mediaDownload(String media_id) throws WxErrorException; | |||
| @@ -216,7 +210,6 @@ public interface WxCpService { | |||
| * </pre> | |||
| * @see #menuGet(String) | |||
| * | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| WxMenu menuGet() throws WxErrorException; | |||
| @@ -231,7 +224,6 @@ public interface WxCpService { | |||
| * @see #menuGet() | |||
| * | |||
| * @param agentId 企业号应用的id | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| WxMenu menuGet(String agentId) throws WxErrorException; | |||
| @@ -255,7 +247,6 @@ public interface WxCpService { | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | |||
| * </pre> | |||
| * | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| List<WxCpDepart> departGet() throws WxErrorException; | |||
| @@ -291,7 +282,6 @@ public interface WxCpService { | |||
| * @param departId 必填。部门id | |||
| * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||
| * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
| @@ -306,7 +296,6 @@ public interface WxCpService { | |||
| * @param departId 必填。部门id | |||
| * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | |||
| * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | |||
| @@ -350,7 +339,6 @@ public interface WxCpService { | |||
| * 获取用户 | |||
| * | |||
| * @param userid | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| WxCpUser userGet(String userid) throws WxErrorException; | |||
| @@ -359,7 +347,6 @@ public interface WxCpService { | |||
| * 创建标签 | |||
| * | |||
| * @param tagName | |||
| * @return | |||
| */ | |||
| String tagCreate(String tagName) throws WxErrorException; | |||
| @@ -381,7 +368,6 @@ public interface WxCpService { | |||
| /** | |||
| * 获得标签列表 | |||
| * | |||
| * @return | |||
| */ | |||
| List<WxCpTag> tagGet() throws WxErrorException; | |||
| @@ -389,7 +375,6 @@ public interface WxCpService { | |||
| * 获取标签成员 | |||
| * | |||
| * @param tagId | |||
| * @return | |||
| */ | |||
| List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException; | |||
| @@ -478,7 +463,6 @@ public interface WxCpService { | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | |||
| * @param url | |||
| * @param queryParam | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| String get(String url, String queryParam) throws WxErrorException; | |||
| @@ -487,7 +471,6 @@ public interface WxCpService { | |||
| * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | |||
| * @param url | |||
| * @param postData | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| String post(String url, String postData) throws WxErrorException; | |||
| @@ -503,7 +486,6 @@ public interface WxCpService { | |||
| * @param data | |||
| * @param <T> | |||
| * @param <E> | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | |||
| @@ -536,7 +518,6 @@ public interface WxCpService { | |||
| /** | |||
| * 获取某个sessionId对应的session,如果sessionId没有对应的session,则新建一个并返回。 | |||
| * @param id id可以为任意字符串,建议使用FromUserName作为id | |||
| * @return | |||
| */ | |||
| WxSession getSession(String id); | |||
| @@ -544,7 +525,6 @@ public interface WxCpService { | |||
| * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | |||
| * @param id id可以为任意字符串,建议使用FromUserName作为id | |||
| * @param create | |||
| * @return | |||
| */ | |||
| WxSession getSession(String id, boolean create); | |||
| @@ -556,25 +536,24 @@ public interface WxCpService { | |||
| * @param sessionManager | |||
| */ | |||
| void setSessionManager(WxSessionManager sessionManager); | |||
| /** | |||
| * 上传部门列表覆盖企业号上的部门信息 | |||
| * @param mediaId | |||
| * @throws WxErrorException | |||
| */ | |||
| String replaceParty(String mediaId) throws WxErrorException; | |||
| /** | |||
| * 上传用户列表覆盖企业号上的用户信息 | |||
| * @param mediaId | |||
| * @throws WxErrorException | |||
| */ | |||
| String replaceUser(String mediaId) throws WxErrorException; | |||
| /** | |||
| * 获取异步任务结果 | |||
| * @param joinId | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| String getTaskResult(String joinId) throws WxErrorException; | |||
| @@ -510,12 +510,6 @@ public class WxCpServiceImpl implements WxCpService { | |||
| /** | |||
| * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | |||
| * | |||
| * @param executor | |||
| * @param uri | |||
| * @param data | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
| int retryTimes = 0; | |||
| @@ -28,7 +28,7 @@ public class WxCpMessage implements Serializable { | |||
| private String hqMusicUrl; | |||
| private String safe; | |||
| private List<WxArticle> articles = new ArrayList<WxArticle>(); | |||
| public String getToUser() { | |||
| return toUser; | |||
| } | |||
| @@ -63,7 +63,7 @@ public class WxCpMessage implements Serializable { | |||
| public String getMsgType() { | |||
| return msgType; | |||
| } | |||
| public String getSafe() { | |||
| return safe; | |||
| } | |||
| @@ -135,18 +135,18 @@ public class WxCpMessage implements Serializable { | |||
| public void setArticles(List<WxArticle> articles) { | |||
| this.articles = articles; | |||
| } | |||
| public String toJson() { | |||
| return WxCpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| public static class WxArticle { | |||
| private String title; | |||
| private String description; | |||
| private String url; | |||
| private String picUrl; | |||
| public String getTitle() { | |||
| return title; | |||
| } | |||
| @@ -171,12 +171,11 @@ public class WxCpMessage implements Serializable { | |||
| public void setPicUrl(String picUrl) { | |||
| this.picUrl = picUrl; | |||
| } | |||
| } | |||
| /** | |||
| * 获得文本消息builder | |||
| * @return | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| @@ -184,7 +183,6 @@ public class WxCpMessage implements Serializable { | |||
| /** | |||
| * 获得图片消息builder | |||
| * @return | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| @@ -192,23 +190,20 @@ public class WxCpMessage implements Serializable { | |||
| /** | |||
| * 获得语音消息builder | |||
| * @return | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| } | |||
| /** | |||
| * 获得视频消息builder | |||
| * @return | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| } | |||
| /** | |||
| * 获得图文消息builder | |||
| * @return | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| @@ -216,10 +211,9 @@ public class WxCpMessage implements Serializable { | |||
| /** | |||
| * 获得文件消息builder | |||
| * @return | |||
| */ | |||
| public static FileBuilder FILE() { | |||
| return new FileBuilder(); | |||
| } | |||
| } | |||
| @@ -198,7 +198,6 @@ public class WxCpXmlMessage implements Serializable { | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | |||
| * </pre> | |||
| * | |||
| * @return | |||
| */ | |||
| public String getMsgType() { | |||
| return msgType; | |||
| @@ -404,7 +403,6 @@ public class WxCpXmlMessage implements Serializable { | |||
| * @param timestamp | |||
| * @param nonce | |||
| * @param msgSignature | |||
| * @return | |||
| */ | |||
| public static WxCpXmlMessage fromEncryptedXml( | |||
| String encryptedXml, | |||
| @@ -542,7 +540,6 @@ public class WxCpXmlMessage implements Serializable { | |||
| /** | |||
| * 扫描类型,一般是qrcode | |||
| * @return | |||
| */ | |||
| public String getScanType() { | |||
| @@ -555,7 +552,6 @@ public class WxCpXmlMessage implements Serializable { | |||
| /** | |||
| * 扫描结果,即二维码对应的字符串信息 | |||
| * @return | |||
| */ | |||
| public String getScanResult() { | |||
| return scanResult; | |||
| @@ -14,14 +14,14 @@ public abstract class WxCpXmlOutMessage { | |||
| @XStreamAlias("ToUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| protected String toUserName; | |||
| @XStreamAlias("FromUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| protected String fromUserName; | |||
| @XStreamAlias("CreateTime") | |||
| protected Long createTime; | |||
| @XStreamAlias("MsgType") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| protected String msgType; | |||
| @@ -57,14 +57,13 @@ public abstract class WxCpXmlOutMessage { | |||
| public void setMsgType(String msgType) { | |||
| this.msgType = msgType; | |||
| } | |||
| protected String toXml() { | |||
| return XStreamTransformer.toXml((Class)this.getClass(), this); | |||
| } | |||
| /** | |||
| * 转换成加密的xml格式 | |||
| * @return | |||
| */ | |||
| public String toEncryptedXml(WxCpConfigStorage wxCpConfigStorage) { | |||
| String plainXml = toXml(); | |||
| @@ -74,7 +73,6 @@ public abstract class WxCpXmlOutMessage { | |||
| /** | |||
| * 获得文本消息builder | |||
| * @return | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| @@ -82,7 +80,6 @@ public abstract class WxCpXmlOutMessage { | |||
| /** | |||
| * 获得图片消息builder | |||
| * @return | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| @@ -90,23 +87,20 @@ public abstract class WxCpXmlOutMessage { | |||
| /** | |||
| * 获得语音消息builder | |||
| * @return | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| } | |||
| /** | |||
| * 获得视频消息builder | |||
| * @return | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| } | |||
| /** | |||
| * 获得图文消息builder | |||
| * @return | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| @@ -14,10 +14,6 @@ public class XStreamTransformer { | |||
| /** | |||
| * xml -> pojo | |||
| * | |||
| * @param clazz | |||
| * @param xml | |||
| * @return | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T fromXml(Class<T> clazz, String xml) { | |||
| @@ -43,9 +39,6 @@ public class XStreamTransformer { | |||
| /** | |||
| * pojo -> xml | |||
| * | |||
| * @param clazz | |||
| * @param object | |||
| * @return | |||
| */ | |||
| public static <T> String toXml(Class<T> clazz, T object) { | |||
| return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); | |||
| @@ -0,0 +1 @@ | |||
| *.pmd | |||
| @@ -0,0 +1,7 @@ | |||
| #### 本Demo使用Spring MVC 框架实现微信公众号后台管理功能,支持多公众号,欢迎帮忙维护添加新功能,或提供更好的实现。 | |||
| ### 1. 配置 | |||
| 复制/src/main/resources/wx-gzh1.properties.template 生成wx-gzh1.properties 文件,填写相关配置; | |||
| 复制/src/main/resources/wx-gzh2.properties.template 生成wx-gzh2.properties 文件,填写相关配置. | |||
| ### 2. 使用maven运行demo程序 | |||
| mvn jetty:run | |||
| @@ -0,0 +1,91 @@ | |||
| <?xml version="1.0"?> | |||
| <project | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" | |||
| xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>2.0.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>weixin-java-demo-with-spring</artifactId> | |||
| <packaging>war</packaging> | |||
| <name>WeiXin Java Tools - DemoWithSpring</name> | |||
| <description>spring demo</description> | |||
| <url>https://github.com/binarywang/weixin-java-tools</url> | |||
| <properties> | |||
| <spring.version>4.3.0.RELEASE</spring.version> | |||
| <spring-security.version>4.1.0.RELEASE</spring-security.version> | |||
| <guava.version>19.0</guava.version> | |||
| <slf4j.version>1.7.2</slf4j.version> | |||
| <aspectj.version>1.8.9</aspectj.version> | |||
| <fastjson.version>1.2.6</fastjson.version> | |||
| <commons-lang3.version>3.4</commons-lang3.version> | |||
| <jetty-maven-plugin.version>9.3.10.v20160621</jetty-maven-plugin.version> | |||
| </properties> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.alibaba</groupId> | |||
| <artifactId>fastjson</artifactId> | |||
| <version>${fastjson.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-lang3</artifactId> | |||
| <version>${commons-lang3.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.google.guava</groupId> | |||
| <artifactId>guava</artifactId> | |||
| <version>${guava.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.slf4j</groupId> | |||
| <artifactId>slf4j-log4j12</artifactId> | |||
| <version>${slf4j.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.springframework</groupId> | |||
| <artifactId>spring-webmvc</artifactId> | |||
| <version>${spring.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.springframework</groupId> | |||
| <artifactId>spring-web</artifactId> | |||
| <version>${spring.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.springframework.security</groupId> | |||
| <artifactId>spring-security-core</artifactId> | |||
| <version>${spring-security.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.aspectj</groupId> | |||
| <artifactId>aspectjrt</artifactId> | |||
| <version>${aspectj.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.aspectj</groupId> | |||
| <artifactId>aspectjweaver</artifactId> | |||
| <version>${aspectj.version}</version> | |||
| </dependency> | |||
| </dependencies> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-maven-plugin</artifactId> | |||
| <version>${jetty-maven-plugin.version}</version> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| </project> | |||
| @@ -0,0 +1,80 @@ | |||
| package com.github.binarywang.demo.spring.aop; | |||
| import org.apache.commons.lang3.ArrayUtils; | |||
| import org.aspectj.lang.JoinPoint; | |||
| import org.aspectj.lang.annotation.After; | |||
| import org.aspectj.lang.annotation.Aspect; | |||
| import org.aspectj.lang.annotation.Before; | |||
| import org.aspectj.lang.annotation.Pointcut; | |||
| import org.aspectj.lang.reflect.CodeSignature; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.security.core.Authentication; | |||
| import org.springframework.security.core.context.SecurityContextHolder; | |||
| import org.springframework.stereotype.Component; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Aspect | |||
| @Component | |||
| public class ControllerLogAspect { | |||
| private Logger logger = LoggerFactory.getLogger(getClass()); | |||
| @Pointcut("within(com.github.binarywang.demo.spring..*.controller..*)") | |||
| public void inController() { | |||
| } | |||
| @Pointcut("execution(public * com.github.binarywang.demo.spring..*.controller..*.*(..))") | |||
| public void controller() { | |||
| } | |||
| @Before("inController()") | |||
| public void writeBeforeLog(JoinPoint jp) { | |||
| this.debugInController(jp, "Start"); | |||
| } | |||
| @After("inController()") | |||
| public void writeAfterLog(JoinPoint jp) { | |||
| this.debugInController(jp, "End"); | |||
| } | |||
| private void debugInController(JoinPoint jp, String msg) { | |||
| String userName = getLoginUserName(); | |||
| this.logger.debug("\n【{}】{}.{}() {} ", userName, | |||
| jp.getTarget() | |||
| .getClass().getSimpleName(), jp.getSignature().getName(), msg); | |||
| } | |||
| private static String getLoginUserName() { | |||
| Authentication authentication = SecurityContextHolder.getContext() | |||
| .getAuthentication(); | |||
| if (authentication != null) { | |||
| return authentication.getName(); | |||
| } | |||
| return "Anonymous"; | |||
| } | |||
| @Before("controller()") | |||
| public void writeParams(JoinPoint jp) { | |||
| String[] names = ((CodeSignature) jp.getSignature()) | |||
| .getParameterNames(); | |||
| Object[] args = jp.getArgs(); | |||
| if (ArrayUtils.isEmpty(names)) { | |||
| return; | |||
| } | |||
| StringBuilder sb = new StringBuilder("Arguments: "); | |||
| for (int i = 0; i < names.length; i++) { | |||
| sb.append(names[i] + " = " + args[i] + ","); | |||
| } | |||
| debugInController(jp, sb.toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| package com.github.binarywang.demo.spring.builder; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class AbstractBuilder { | |||
| protected final Logger logger = LoggerFactory.getLogger(getClass()); | |||
| public abstract WxMpXmlOutMessage build(String content, | |||
| WxMpXmlMessage wxMessage, BaseWxService service) ; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| package com.github.binarywang.demo.spring.builder; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class ImageBuilder extends AbstractBuilder { | |||
| @Override | |||
| public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, | |||
| BaseWxService service) { | |||
| WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId(content) | |||
| .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) | |||
| .build(); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.github.binarywang.demo.spring.builder; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class TextBuilder extends AbstractBuilder { | |||
| @Override | |||
| public WxMpXmlOutMessage build(String content, WxMpXmlMessage wxMessage, | |||
| BaseWxService service) { | |||
| WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content(content) | |||
| .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) | |||
| .build(); | |||
| return m; | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| package com.github.binarywang.demo.spring.config; | |||
| /** | |||
| * 公众号标识的枚举类 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public enum WxAccountEnum { | |||
| GZH1(1, "公众号1"), | |||
| GZH2(2, "公众号2"); | |||
| private int pubid; | |||
| private String name; | |||
| private WxAccountEnum(int pubid, String name) { | |||
| this.name = name; | |||
| this.pubid = pubid; | |||
| } | |||
| public int getPubid() { | |||
| return this.pubid; | |||
| } | |||
| public String getName() { | |||
| return this.name; | |||
| } | |||
| public static int queryPubid(String wxCode) { | |||
| return WxAccountEnum.valueOf(wxCode.toUpperCase()).getPubid(); | |||
| } | |||
| public static String queryWxCode(int pubid) { | |||
| for (WxAccountEnum e : values()) { | |||
| if (e.getPubid() == pubid) { | |||
| return e.name().toLowerCase(); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.config; | |||
| /** | |||
| * 微信配置的抽象实现类 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class WxConfig { | |||
| public abstract String getToken(); | |||
| public abstract String getAppid(); | |||
| public abstract String getAppsecret(); | |||
| public abstract String getAesKey(); | |||
| public abstract WxAccountEnum getWxAccountEnum(); | |||
| public int getPubId() { | |||
| return getWxAccountEnum().getPubid(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| package com.github.binarywang.demo.spring.config; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.context.annotation.Configuration; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Configuration | |||
| public class WxGzh1Config extends WxConfig { | |||
| @Value("#{gzh1WxProperties.wx_token}") | |||
| private String token; | |||
| @Value("#{gzh1WxProperties.wx_appid}") | |||
| private String appid; | |||
| @Value("#{gzh1WxProperties.wx_appsecret}") | |||
| private String appsecret; | |||
| @Value("#{gzh1WxProperties.wx_aeskey}") | |||
| private String aesKey; | |||
| @Override | |||
| public String getToken() { | |||
| return this.token; | |||
| } | |||
| @Override | |||
| public String getAppid() { | |||
| return this.appid; | |||
| } | |||
| @Override | |||
| public String getAppsecret() { | |||
| return this.appsecret; | |||
| } | |||
| @Override | |||
| public String getAesKey() { | |||
| return this.aesKey; | |||
| } | |||
| @Override | |||
| public WxAccountEnum getWxAccountEnum() { | |||
| return WxAccountEnum.GZH1; | |||
| } | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| package com.github.binarywang.demo.spring.config; | |||
| import org.springframework.beans.factory.annotation.Value; | |||
| import org.springframework.context.annotation.Configuration; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Configuration | |||
| public class WxGzh2Config extends WxConfig { | |||
| @Value("#{gzh2WxProperties.wx_token}") | |||
| private String token; | |||
| @Value("#{gzh2WxProperties.wx_appid}") | |||
| private String appid; | |||
| @Value("#{gzh2WxProperties.wx_appsecret}") | |||
| private String appsecret; | |||
| @Value("#{gzh2WxProperties.wx_aeskey}") | |||
| private String aesKey; | |||
| @Override | |||
| public String getToken() { | |||
| return this.token; | |||
| } | |||
| @Override | |||
| public String getAppid() { | |||
| return this.appid; | |||
| } | |||
| @Override | |||
| public String getAppsecret() { | |||
| return this.appsecret; | |||
| } | |||
| @Override | |||
| public String getAesKey() { | |||
| return this.aesKey; | |||
| } | |||
| @Override | |||
| public WxAccountEnum getWxAccountEnum() { | |||
| return WxAccountEnum.GZH2; | |||
| } | |||
| } | |||
| @@ -0,0 +1,80 @@ | |||
| package com.github.binarywang.demo.spring.controller; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.web.bind.annotation.RequestBody; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RequestMethod; | |||
| import org.springframework.web.bind.annotation.RequestParam; | |||
| import org.springframework.web.bind.annotation.ResponseBody; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class AbstractWxPortalController { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| @RequestMapping(method = RequestMethod.GET, produces = "text/plain;charset=utf-8") | |||
| public @ResponseBody String authGet( | |||
| @RequestParam("signature") String signature, | |||
| @RequestParam("timestamp") String timestamp, | |||
| @RequestParam("nonce") String nonce, | |||
| @RequestParam("echostr") String echostr) { | |||
| this.logger.info("\n接收到来自微信服务器的认证消息:[{},{},{},{}]", | |||
| signature, timestamp, nonce, echostr); | |||
| if (this.getWxService().checkSignature(timestamp, nonce, signature)) { | |||
| return echostr; | |||
| } | |||
| return "非法请求"; | |||
| } | |||
| @RequestMapping(method = RequestMethod.POST, produces = "application/xml; charset=UTF-8") | |||
| public @ResponseBody String post(@RequestBody String requestBody, | |||
| @RequestParam("signature") String signature, | |||
| @RequestParam("encrypt_type") String encType, | |||
| @RequestParam("msg_signature") String msgSignature, | |||
| @RequestParam("timestamp") String timestamp, | |||
| @RequestParam("nonce") String nonce) { | |||
| this.logger.info("\n接收微信请求:[{},{},{},{},{}]\n{} ", | |||
| signature, encType, msgSignature, timestamp, nonce, requestBody); | |||
| String out = null; | |||
| if (encType == null) { | |||
| // 明文传输的消息 | |||
| WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); | |||
| WxMpXmlOutMessage outMessage = this.getWxService().route(inMessage); | |||
| if (outMessage == null) { | |||
| return ""; | |||
| } | |||
| out = outMessage.toXml(); | |||
| }else if ("aes".equals(encType)) { | |||
| // aes加密的消息 | |||
| WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, | |||
| this.getWxService().getWxMpConfigStorage(), timestamp, nonce, msgSignature); | |||
| this.logger.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); | |||
| WxMpXmlOutMessage outMessage = this.getWxService().route(inMessage); | |||
| if (outMessage == null) { | |||
| return ""; | |||
| } | |||
| out = outMessage.toEncryptedXml(this.getWxService().getWxMpConfigStorage()); | |||
| } | |||
| this.logger.debug("\n组装回复信息:{}", out); | |||
| return out; | |||
| } | |||
| protected abstract BaseWxService getWxService(); | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.github.binarywang.demo.spring.controller; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import com.github.binarywang.demo.spring.service.Gzh1WxService; | |||
| /** | |||
| * 第一个公众号的微信交互接口 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @RestController | |||
| @RequestMapping("/api/gzh1/portal") | |||
| public class Gzh1WxPortalController extends AbstractWxPortalController{ | |||
| @Autowired | |||
| private Gzh1WxService wxService; | |||
| @Override | |||
| protected BaseWxService getWxService() { | |||
| return this.wxService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.github.binarywang.demo.spring.controller; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.web.bind.annotation.RequestMapping; | |||
| import org.springframework.web.bind.annotation.RestController; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import com.github.binarywang.demo.spring.service.Gzh2WxService; | |||
| /** | |||
| * 第二个公众号的微信交互接口 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @RestController | |||
| @RequestMapping("/api/gzh2/portal") | |||
| public class Gzh2WxPortalController extends AbstractWxPortalController{ | |||
| @Autowired | |||
| private Gzh2WxService wxService; | |||
| @Override | |||
| protected BaseWxService getWxService() { | |||
| return this.wxService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| package com.github.binarywang.demo.spring.dto; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| /** | |||
| * 菜单的dto对象 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMenuKey { | |||
| private String type; | |||
| private String content; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this); | |||
| } | |||
| public WxMenuKey() { | |||
| } | |||
| public WxMenuKey(String type, String content) { | |||
| this.type = type; | |||
| this.content = content; | |||
| } | |||
| public String getType() { | |||
| return this.type; | |||
| } | |||
| public void setType(String type) { | |||
| this.type = type; | |||
| } | |||
| public String getContent() { | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| this.content = content; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.google.gson.Gson; | |||
| import me.chanjar.weixin.mp.api.WxMpMessageHandler; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class AbstractHandler implements WxMpMessageHandler { | |||
| protected Logger logger = LoggerFactory.getLogger(getClass()); | |||
| protected final Gson gson = new Gson(); | |||
| protected abstract WxConfig getWxConfig(); | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Component | |||
| public class KfSessionHandler extends AbstractHandler{ | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) throws WxErrorException { | |||
| //TODO 对会话做处理 | |||
| return null; | |||
| } | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import com.github.binarywang.demo.spring.builder.TextBuilder; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class LocationHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) throws WxErrorException { | |||
| if (wxMessage.getMsgType().equals(WxConsts.XML_MSG_LOCATION)) { | |||
| //TODO 接收处理用户发送的地理位置消息 | |||
| try { | |||
| String content = "感谢反馈,您的的地理位置已收到!"; | |||
| return new TextBuilder().build(content, wxMessage, null); | |||
| } catch (Exception e) { | |||
| this.logger.error("位置消息接收处理失败", e); | |||
| return null; | |||
| } | |||
| } | |||
| //上报地理位置事件 | |||
| this.logger.info("\n上报地理位置 。。。 "); | |||
| this.logger.info("\n纬度 : " + wxMessage.getLatitude()); | |||
| this.logger.info("\n经度 : " + wxMessage.getLongitude()); | |||
| this.logger.info("\n精度 : " + String.valueOf(wxMessage.getPrecision())); | |||
| //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Component | |||
| public class LogHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) { | |||
| this.logger.info("\n接收到请求消息,内容:【{}】", wxMessage.toString()); | |||
| return null; | |||
| } | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import com.alibaba.fastjson.JSON; | |||
| import com.github.binarywang.demo.spring.builder.AbstractBuilder; | |||
| import com.github.binarywang.demo.spring.builder.ImageBuilder; | |||
| import com.github.binarywang.demo.spring.builder.TextBuilder; | |||
| import com.github.binarywang.demo.spring.dto.WxMenuKey; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class MenuHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) { | |||
| BaseWxService weixinService = (BaseWxService) wxMpService; | |||
| String key = wxMessage.getEventKey(); | |||
| WxMenuKey menuKey = null; | |||
| try { | |||
| menuKey = JSON.parseObject(key, WxMenuKey.class); | |||
| } catch (Exception e) { | |||
| return WxMpXmlOutMessage.TEXT().content(key) | |||
| .fromUser(wxMessage.getToUserName()) | |||
| .toUser(wxMessage.getFromUserName()).build(); | |||
| } | |||
| AbstractBuilder builder = null; | |||
| switch (menuKey.getType()) { | |||
| case WxConsts.XML_MSG_TEXT: | |||
| builder = new TextBuilder(); | |||
| break; | |||
| case WxConsts.XML_MSG_IMAGE: | |||
| builder = new ImageBuilder(); | |||
| break; | |||
| case WxConsts.XML_MSG_VOICE: | |||
| break; | |||
| case WxConsts.XML_MSG_VIDEO: | |||
| break; | |||
| case WxConsts.XML_MSG_NEWS: | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| if (builder != null) { | |||
| try { | |||
| return builder.build(menuKey.getContent(), wxMessage, weixinService); | |||
| } catch (Exception e) { | |||
| this.logger.error(e.getMessage(), e); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import org.apache.commons.lang3.StringUtils; | |||
| import com.github.binarywang.demo.spring.builder.TextBuilder; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class MsgHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) { | |||
| BaseWxService weixinService = (BaseWxService) wxMpService; | |||
| if (!wxMessage.getMsgType().equals(WxConsts.XML_MSG_EVENT)) { | |||
| //TODO 可以选择将消息保存到本地 | |||
| } | |||
| //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服 | |||
| if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服") | |||
| && weixinService.hasKefuOnline()) { | |||
| return WxMpXmlOutMessage | |||
| .TRANSFER_CUSTOMER_SERVICE().fromUser(wxMessage.getToUserName()) | |||
| .toUser(wxMessage.getFromUserName()).build(); | |||
| } | |||
| //TODO 组装回复消息 | |||
| String content = "回复信息内容"; | |||
| return new TextBuilder().build(content, wxMessage, weixinService); | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Component | |||
| public class NullHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) { | |||
| return null; | |||
| } | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class ScanHandler extends AbstractHandler { | |||
| } | |||
| @@ -0,0 +1,61 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import com.github.binarywang.demo.spring.builder.TextBuilder; | |||
| import com.github.binarywang.demo.spring.service.BaseWxService; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
| import java.util.Map; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class SubscribeHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) throws WxErrorException { | |||
| this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUserName()); | |||
| BaseWxService weixinService = (BaseWxService) wxMpService; | |||
| // 获取微信用户基本信息 | |||
| WxMpUser userWxInfo = weixinService.getUserService().userInfo(wxMessage.getFromUserName(), null); | |||
| if (userWxInfo != null) { | |||
| // TODO 可以添加关注用户到本地 | |||
| } | |||
| WxMpXmlOutMessage responseResult = null; | |||
| try { | |||
| responseResult = handleSpecial(wxMessage); | |||
| } catch (Exception e) { | |||
| this.logger.error(e.getMessage(), e); | |||
| } | |||
| if (responseResult != null) { | |||
| return responseResult; | |||
| } | |||
| try { | |||
| return new TextBuilder().build("感谢关注", wxMessage, weixinService); | |||
| } catch (Exception e) { | |||
| this.logger.error(e.getMessage(), e); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 | |||
| */ | |||
| protected abstract WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) throws Exception; | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| package com.github.binarywang.demo.spring.handler; | |||
| import java.util.Map; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class UnsubscribeHandler extends AbstractHandler { | |||
| @Override | |||
| public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, | |||
| Map<String, Object> context, WxMpService wxMpService, | |||
| WxSessionManager sessionManager) { | |||
| String openId = wxMessage.getFromUserName(); | |||
| this.logger.info("取消关注用户 OPENID: " + openId); | |||
| // TODO 可以更新本地数据库为取消关注状态 | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.handler.gzh1; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | |||
| import org.springframework.context.annotation.Scope; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.handler.LocationHandler; | |||
| @Component | |||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | |||
| public class Gzh1LocationHandler extends LocationHandler { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.handler.gzh1; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | |||
| import org.springframework.context.annotation.Scope; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.handler.MenuHandler; | |||
| @Component | |||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | |||
| public class Gzh1MenuHandler extends MenuHandler { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.handler.gzh1; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | |||
| import org.springframework.context.annotation.Scope; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.handler.MsgHandler; | |||
| @Component | |||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | |||
| public class Gzh1MsgHandler extends MsgHandler { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| package com.github.binarywang.demo.spring.handler.gzh1; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | |||
| import org.springframework.context.annotation.Scope; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.handler.SubscribeHandler; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| @Component | |||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | |||
| public class Gzh1SubscribeHandler extends SubscribeHandler { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| @Override | |||
| protected WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) | |||
| throws Exception { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.github.binarywang.demo.spring.handler.gzh1; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.beans.factory.config.ConfigurableBeanFactory; | |||
| import org.springframework.context.annotation.Scope; | |||
| import org.springframework.stereotype.Component; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.handler.UnsubscribeHandler; | |||
| @Component | |||
| @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) | |||
| public class Gzh1UnSubscribeHandler extends UnsubscribeHandler { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Override | |||
| protected WxConfig getWxConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| } | |||
| @@ -0,0 +1,138 @@ | |||
| package com.github.binarywang.demo.spring.service; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.handler.*; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage; | |||
| import me.chanjar.weixin.mp.api.WxMpMessageRouter; | |||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||
| import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import javax.annotation.PostConstruct; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public abstract class BaseWxService extends WxMpServiceImpl { | |||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | |||
| @Autowired | |||
| protected LogHandler logHandler; | |||
| @Autowired | |||
| protected NullHandler nullHandler; | |||
| @Autowired | |||
| protected KfSessionHandler kfSessionHandler; | |||
| private WxMpMessageRouter router; | |||
| protected abstract WxConfig getServerConfig(); | |||
| protected abstract MenuHandler getMenuHandler(); | |||
| protected abstract SubscribeHandler getSubscribeHandler(); | |||
| protected abstract UnsubscribeHandler getUnsubscribeHandler(); | |||
| protected abstract AbstractHandler getLocationHandler(); | |||
| protected abstract MsgHandler getMsgHandler(); | |||
| protected abstract AbstractHandler getScanHandler(); | |||
| @PostConstruct | |||
| public void init() { | |||
| final WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage(); | |||
| config.setAppId(this.getServerConfig().getAppid());// 设置微信公众号的appid | |||
| config.setSecret(this.getServerConfig().getAppsecret());// 设置微信公众号的app corpSecret | |||
| config.setToken(this.getServerConfig().getToken());// 设置微信公众号的token | |||
| config.setAesKey(this.getServerConfig().getAesKey());// 设置消息加解密密钥 | |||
| super.setWxMpConfigStorage(config); | |||
| this.refreshRouter(); | |||
| } | |||
| private void refreshRouter() { | |||
| final WxMpMessageRouter newRouter = new WxMpMessageRouter(this); | |||
| // 记录所有事件的日志 | |||
| newRouter.rule().handler(this.logHandler).next(); | |||
| // 接收客服会话管理事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_KF_CREATE_SESSION).handler(this.kfSessionHandler).end(); | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_KF_CLOSE_SESSION).handler(this.kfSessionHandler).end(); | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_KF_SWITCH_SESSION).handler(this.kfSessionHandler).end(); | |||
| // 自定义菜单事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.BUTTON_CLICK).handler(this.getMenuHandler()).end(); | |||
| // 点击菜单连接事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.BUTTON_VIEW).handler(this.nullHandler).end(); | |||
| // 关注事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_SUBSCRIBE).handler(this.getSubscribeHandler()) | |||
| .end(); | |||
| // 取消关注事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_UNSUBSCRIBE).handler(this.getUnsubscribeHandler()) | |||
| .end(); | |||
| // 上报地理位置事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_LOCATION).handler(this.getLocationHandler()).end(); | |||
| // 接收地理位置消息 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_LOCATION) | |||
| .handler(this.getLocationHandler()).end(); | |||
| // 扫码事件 | |||
| newRouter.rule().async(false).msgType(WxConsts.XML_MSG_EVENT) | |||
| .event(WxConsts.EVT_SCAN).handler(this.getScanHandler()).end(); | |||
| // 默认 | |||
| newRouter.rule().async(false).handler(this.getMsgHandler()).end(); | |||
| this.router = newRouter; | |||
| } | |||
| public WxMpXmlOutMessage route(WxMpXmlMessage message) { | |||
| try { | |||
| return this.router.route(message); | |||
| } catch (Exception e) { | |||
| this.logger.error(e.getMessage(), e); | |||
| } | |||
| return null; | |||
| } | |||
| public boolean hasKefuOnline() { | |||
| try { | |||
| WxMpKfOnlineList kfOnlineList = this.getKefuService().kfOnlineList(); | |||
| return kfOnlineList != null && kfOnlineList.getKfOnlineList().size() > 0; | |||
| } catch (Exception e) { | |||
| this.logger.error("获取客服在线状态异常: " + e.getMessage(), e); | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| package com.github.binarywang.demo.spring.service; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import com.github.binarywang.demo.spring.config.WxGzh1Config; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.handler.AbstractHandler; | |||
| import com.github.binarywang.demo.spring.handler.MenuHandler; | |||
| import com.github.binarywang.demo.spring.handler.MsgHandler; | |||
| import com.github.binarywang.demo.spring.handler.SubscribeHandler; | |||
| import com.github.binarywang.demo.spring.handler.UnsubscribeHandler; | |||
| import com.github.binarywang.demo.spring.handler.gzh1.Gzh1LocationHandler; | |||
| import com.github.binarywang.demo.spring.handler.gzh1.Gzh1MenuHandler; | |||
| import com.github.binarywang.demo.spring.handler.gzh1.Gzh1MsgHandler; | |||
| import com.github.binarywang.demo.spring.handler.gzh1.Gzh1SubscribeHandler; | |||
| import com.github.binarywang.demo.spring.handler.gzh1.Gzh1UnSubscribeHandler; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Service | |||
| public class Gzh1WxService extends BaseWxService { | |||
| @Autowired | |||
| private WxGzh1Config wxConfig; | |||
| @Autowired | |||
| private Gzh1LocationHandler locationHandler; | |||
| @Autowired | |||
| private Gzh1MenuHandler menuHandler; | |||
| @Autowired | |||
| private Gzh1MsgHandler msgHandler; | |||
| @Autowired | |||
| private Gzh1UnSubscribeHandler unSubscribeHandler; | |||
| @Autowired | |||
| private Gzh1SubscribeHandler subscribeHandler; | |||
| @Override | |||
| protected WxConfig getServerConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| @Override | |||
| protected MenuHandler getMenuHandler() { | |||
| return this.menuHandler; | |||
| } | |||
| @Override | |||
| protected SubscribeHandler getSubscribeHandler() { | |||
| return this.subscribeHandler; | |||
| } | |||
| @Override | |||
| protected UnsubscribeHandler getUnsubscribeHandler() { | |||
| return this.unSubscribeHandler; | |||
| } | |||
| @Override | |||
| protected AbstractHandler getLocationHandler() { | |||
| return this.locationHandler; | |||
| } | |||
| @Override | |||
| protected MsgHandler getMsgHandler() { | |||
| return this.msgHandler; | |||
| } | |||
| @Override | |||
| protected AbstractHandler getScanHandler() { | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| package com.github.binarywang.demo.spring.service; | |||
| import org.springframework.beans.factory.annotation.Autowired; | |||
| import org.springframework.stereotype.Service; | |||
| import com.github.binarywang.demo.spring.config.WxGzh2Config; | |||
| import com.github.binarywang.demo.spring.config.WxConfig; | |||
| import com.github.binarywang.demo.spring.handler.AbstractHandler; | |||
| import com.github.binarywang.demo.spring.handler.MenuHandler; | |||
| import com.github.binarywang.demo.spring.handler.MsgHandler; | |||
| import com.github.binarywang.demo.spring.handler.SubscribeHandler; | |||
| import com.github.binarywang.demo.spring.handler.UnsubscribeHandler; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| @Service | |||
| public class Gzh2WxService extends BaseWxService { | |||
| @Autowired | |||
| private WxGzh2Config wxConfig; | |||
| @Override | |||
| protected WxConfig getServerConfig() { | |||
| return this.wxConfig; | |||
| } | |||
| @Override | |||
| protected MenuHandler getMenuHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| @Override | |||
| protected SubscribeHandler getSubscribeHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| @Override | |||
| protected UnsubscribeHandler getUnsubscribeHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| @Override | |||
| protected AbstractHandler getLocationHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| @Override | |||
| protected MsgHandler getMsgHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| @Override | |||
| protected AbstractHandler getScanHandler() { | |||
| // TODO Auto-generated method stub | |||
| return null; | |||
| } | |||
| } | |||
| @@ -0,0 +1,2 @@ | |||
| /wx-gzh1.properties | |||
| /wx-gzh2.properties | |||
| @@ -0,0 +1,17 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <beans xmlns="http://www.springframework.org/schema/beans" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" | |||
| xmlns:context="http://www.springframework.org/schema/context" | |||
| xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util" | |||
| xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd | |||
| http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd | |||
| http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd | |||
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd | |||
| http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> | |||
| <import resource="classpath*:spring-service-bean.xml" /> | |||
| <util:properties id="gzh1WxProperties" location="classpath:/wx-gzh1.properties" /> | |||
| <util:properties id="gzh2WxProperties" location="classpath:/wx-gzh2.properties" /> | |||
| </beans> | |||
| @@ -0,0 +1,8 @@ | |||
| log4j.rootLogger=DEBUG, stdout | |||
| log4j.appender.stdout=org.apache.log4j.ConsoleAppender | |||
| log4j.appender.stdout.Target=System.out | |||
| log4j.appender.stdout.layout=org.apache.log4j.EnhancedPatternLayout | |||
| log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} [%t]:%c:%L#%M() %m%n | |||
| log4j.logger.org.apache.http.impl.conn.PoolingHttpClientConnectionManager=INFO | |||
| @@ -0,0 +1,29 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <beans xmlns="http://www.springframework.org/schema/beans" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" | |||
| xmlns:amq="http://activemq.apache.org/schema/core" xmlns:util="http://www.springframework.org/schema/util" | |||
| xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd | |||
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd | |||
| http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> | |||
| <context:component-scan base-package="com.github.binarywang.demo.spring"> | |||
| <context:exclude-filter type="annotation" | |||
| expression="org.springframework.stereotype.Controller" /> | |||
| </context:component-scan> | |||
| <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> | |||
| <property name="messageConverters"> | |||
| <list> | |||
| <bean | |||
| class="org.springframework.http.converter.StringHttpMessageConverter"> | |||
| <constructor-arg> | |||
| <value>UTF-8</value> | |||
| </constructor-arg> | |||
| </bean> | |||
| <bean | |||
| class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" /> | |||
| </list> | |||
| </property> | |||
| </bean> | |||
| </beans> | |||
| @@ -0,0 +1,43 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <beans xmlns="http://www.springframework.org/schema/beans" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" | |||
| xmlns:context="http://www.springframework.org/schema/context" | |||
| xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" | |||
| xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd | |||
| http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd | |||
| http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd | |||
| http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd | |||
| http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> | |||
| <!-- only scan the Controller class for web context --> | |||
| <context:component-scan base-package="com.github.binarywang.demo.spring"> | |||
| <context:include-filter type="annotation" | |||
| expression="org.springframework.stereotype.Controller" /> | |||
| </context:component-scan> | |||
| <mvc:annotation-driven> | |||
| <mvc:message-converters> | |||
| <ref bean="jsonConverter" /> | |||
| <ref bean="stringHttpMessageConverter" /> | |||
| </mvc:message-converters> | |||
| </mvc:annotation-driven> | |||
| <mvc:default-servlet-handler /> | |||
| <bean id="stringHttpMessageConverter" | |||
| class="org.springframework.http.converter.StringHttpMessageConverter"> | |||
| <property name="supportedMediaTypes"> | |||
| <list> | |||
| <value>text/html;charset=UTF-8</value> | |||
| <value>text/plain;charset=UTF-8</value> | |||
| </list> | |||
| </property> | |||
| </bean> | |||
| <bean id="jsonConverter" | |||
| class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> | |||
| <property name="supportedMediaTypes" value="application/json" /> | |||
| </bean> | |||
| <aop:aspectj-autoproxy proxy-target-class="true" /> | |||
| </beans> | |||
| @@ -0,0 +1,4 @@ | |||
| wx_appid= | |||
| wx_appsecret= | |||
| wx_token= | |||
| wx_aeskey= | |||
| @@ -0,0 +1,4 @@ | |||
| wx_appid= | |||
| wx_appsecret= | |||
| wx_token= | |||
| wx_aeskey= | |||
| @@ -20,14 +20,6 @@ | |||
| <filter-name>encodingFilter</filter-name> | |||
| <url-pattern>/*</url-pattern> | |||
| </filter-mapping> | |||
| <filter> | |||
| <filter-name>springSecurityFilterChain</filter-name> | |||
| <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | |||
| </filter> | |||
| <filter-mapping> | |||
| <filter-name>springSecurityFilterChain</filter-name> | |||
| <url-pattern>/v1/admin/*</url-pattern> | |||
| </filter-mapping> | |||
| <listener> | |||
| <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> | |||
| @@ -6,7 +6,7 @@ | |||
| <parent> | |||
| <groupId>com.github.binarywang</groupId> | |||
| <artifactId>weixin-java-parent</artifactId> | |||
| <version>1.3.5</version> | |||
| <version>2.0.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>weixin-java-mp</artifactId> | |||
| <name>WeiXin Java Tools - MP</name> | |||
| @@ -47,6 +47,12 @@ | |||
| <groupId>org.eclipse.jetty</groupId> | |||
| <artifactId>jetty-servlet</artifactId> | |||
| <scope>test</scope> | |||
| </dependency><!-- https://mvnrepository.com/artifact/joda-time/joda-time --> | |||
| <dependency> | |||
| <groupId>joda-time</groupId> | |||
| <artifactId>joda-time</artifactId> | |||
| <version>2.9.4</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| </dependencies> | |||
| @@ -0,0 +1,69 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.WxMpGroup; | |||
| import java.util.List; | |||
| /** | |||
| * 用户分组相关操作接口 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public interface WxMpGroupService { | |||
| /** | |||
| * <pre> | |||
| * 分组管理接口 - 创建分组 | |||
| * 最多支持创建500个分组 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口 | |||
| * </pre> | |||
| * | |||
| * @param name 分组名字(30个字符以内) | |||
| */ | |||
| public WxMpGroup groupCreate(String name) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分组管理接口 - 查询所有分组 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口 | |||
| * </pre> | |||
| */ | |||
| public List<WxMpGroup> groupGet() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分组管理接口 - 查询用户所在分组 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口 | |||
| * </pre> | |||
| * | |||
| * @param openid 微信用户的openid | |||
| */ | |||
| public long userGetGroup(String openid) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分组管理接口 - 修改分组名 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口 | |||
| * | |||
| * 如果id为0(未分组),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误 | |||
| * </pre> | |||
| * | |||
| * @param group 要更新的group,group的id,name必须设置 | |||
| */ | |||
| public void groupUpdate(WxMpGroup group) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分组管理接口 - 移动用户分组 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口 | |||
| * | |||
| * 如果to_groupid为0(未分组),1(黑名单),2(星标组),或者不存在的id,微信会返回系统繁忙的错误 | |||
| * </pre> | |||
| * | |||
| * @param openid 用户openid | |||
| * @param to_groupid 移动到的分组id | |||
| */ | |||
| public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException; | |||
| } | |||
| @@ -0,0 +1,172 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.*; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| /** | |||
| * 客服接口 , | |||
| * 命名采用kefu拼音的原因是: | |||
| * 其英文CustomerService如果再加上Service后缀显得有点啰嗦, | |||
| * 如果不加又显得表意不完整 | |||
| * | |||
| * @author Binary Wang | |||
| */ | |||
| public interface WxMpKefuService { | |||
| //*******************客服管理接口***********************// | |||
| /** | |||
| * <pre> | |||
| * 获取客服基本信息 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| WxMpKfList kfList() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取在线客服接待信息 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| WxMpKfOnlineList kfOnlineList() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 添加客服账号 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| boolean kfAccountAdd(WxMpKfAccountRequest request) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 设置客服信息(即更新客服信息) | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| boolean kfAccountUpdate(WxMpKfAccountRequest request) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 设置客服信息(即更新客服信息) | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/inviteworker?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 上传客服头像 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | |||
| * </pre> | |||
| */ | |||
| boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) | |||
| throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 删除客服账号 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN">客服管理</a> | |||
| * 接口url格式:https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | |||
| * </pre> | |||
| */ | |||
| boolean kfAccountDel(String kfAccount) throws WxErrorException; | |||
| //*******************客服会话控制接口***********************// | |||
| /** | |||
| * <pre> | |||
| * 创建会话 | |||
| * 此接口在客服和用户之间创建一个会话,如果该客服和用户会话已存在,则直接返回0。指定的客服帐号必须已经绑定微信号且在线。 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/create?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| boolean kfSessionCreate(String openid, String kfAccount) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 关闭会话 | |||
| * 开发者可以使用本接口,关闭一个会话。 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/close?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| boolean kfSessionClose(String openid, String kfAccount) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取客户的会话状态 | |||
| * 此接口获取一个客户的会话,如果不存在,则kf_account为空。 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制接口</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsession?access_token=ACCESS_TOKEN&openid=OPENID | |||
| * </pre> | |||
| */ | |||
| WxMpKfSessionGetResult kfSessionGet(String openid) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取客服的会话列表 | |||
| * 开发者可以通过本接口获取某个客服正在接待的会话列表。 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getsessionlist?access_token=ACCESS_TOKEN&kf_account=KFACCOUNT | |||
| * </pre> | |||
| */ | |||
| WxMpKfSessionList kfSessionList(String kfAccount) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取未接入会话列表 | |||
| * 开发者可以通过本接口获取当前正在等待队列中的会话列表,此接口最多返回最早进入队列的100个未接入会话。 | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN">客服会话控制</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/kfsession/getwaitcase?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| */ | |||
| WxMpKfSessionWaitCaseList kfSessionGetWaitCase() throws WxErrorException; | |||
| //*******************获取聊天记录的接口***********************// | |||
| /** | |||
| * <pre> | |||
| * 获取聊天记录(原始接口) | |||
| * 此接口返回的聊天记录中,对于图片、语音、视频,分别展示成文本格式的[image]、[voice]、[video] | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1464937269_mUtmK&token=&lang=zh_CN">获取聊天记录</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * | |||
| * @param startTime 起始时间 | |||
| * @param endTime 结束时间 | |||
| * @param msgId 消息id顺序从小到大,从1开始 | |||
| * @param number 每次获取条数,最多10000条 | |||
| * @return 聊天记录对象 | |||
| * @throws WxErrorException | |||
| */ | |||
| WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取聊天记录(优化接口,返回指定时间段内所有的聊天记录) | |||
| * 此接口返回的聊天记录中,对于图片、语音、视频,分别展示成文本格式的[image]、[voice]、[video] | |||
| * 详情请见:<a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1464937269_mUtmK&token=&lang=zh_CN">获取聊天记录</a> | |||
| * 接口url格式: https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token=ACCESS_TOKEN | |||
| * </pre> | |||
| * | |||
| * @param startTime 起始时间 | |||
| * @param endTime 结束时间 | |||
| * @return 聊天记录对象 | |||
| * @throws WxErrorException | |||
| */ | |||
| WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException; | |||
| } | |||
| @@ -0,0 +1,195 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.*; | |||
| import me.chanjar.weixin.mp.bean.result.*; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| * 素材管理的相关接口,包括媒体管理的接口, | |||
| * 即以https://api.weixin.qq.com/cgi-bin/material | |||
| * 和 https://api.weixin.qq.com/cgi-bin/media开头的接口 | |||
| */ | |||
| public interface WxMpMaterialService { | |||
| /** | |||
| * 新增临时素材 | |||
| * | |||
| * @param mediaType | |||
| * @param file | |||
| * @throws WxErrorException | |||
| * @see #mediaUpload(String, String, InputStream) | |||
| */ | |||
| public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取临时素材 | |||
| * 本接口即为原“下载多媒体文件”接口。 | |||
| * 根据微信文档,视频文件下载不了,会返回null | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/9/677a85e3f3849af35de54bb5516c2521.html">获取临时素材</a> | |||
| * </pre> | |||
| * | |||
| * @param media_id | |||
| * @return 保存到本地的临时文件 | |||
| * @throws WxErrorException | |||
| */ | |||
| public File mediaDownload(String media_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 上传图文消息内的图片获取URL | |||
| * 详情请见:http://mp.weixin.qq.com/wiki/15/40b6865b893947b764e2de8e4a1fb55f.html#.E4.B8.8A.E4.BC.A0.E5.9B.BE.E6.96.87.E6.B6.88.E6.81.AF.E5.86.85.E7.9A.84.E5.9B.BE.E7.89.87.E8.8E.B7.E5.8F.96URL.E3.80.90.E8.AE.A2.E9.98.85.E5.8F.B7.E4.B8.8E.E6.9C.8D.E5.8A.A1.E5.8F.B7.E8.AE.A4.E8.AF.81.E5.90.8E.E5.9D.87.E5.8F.AF.E7.94.A8.E3.80.91 | |||
| * </pre> | |||
| * | |||
| * @param file | |||
| * @return WxMediaImgUploadResult 返回图片url | |||
| * @throws WxErrorException | |||
| */ | |||
| public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 新增临时素材 | |||
| * 本接口即为原“上传多媒体文件”接口。 | |||
| * | |||
| * 上传的多媒体文件有格式和大小限制,如下: | |||
| * 图片(image): 1M,支持JPG格式 | |||
| * 语音(voice):2M,播放长度不超过60s,支持AMR\MP3格式 | |||
| * 视频(video):10MB,支持MP4格式 | |||
| * 缩略图(thumb):64KB,支持JPG格式 | |||
| * | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/15/2d353966323806a202cd2deaafe8e557.html">新增临时素材</a> | |||
| * </pre> | |||
| * | |||
| * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} | |||
| * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} | |||
| * @param inputStream 输入流 | |||
| * @throws WxErrorException | |||
| */ | |||
| public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException; | |||
| /** | |||
| * <pre> | |||
| * 上传非图文永久素材 | |||
| * | |||
| * 上传的多媒体文件有格式和大小限制,如下: | |||
| * 图片(image): 图片大小不超过2M,支持bmp/png/jpeg/jpg/gif格式 | |||
| * 语音(voice):语音大小不超过5M,长度不超过60秒,支持mp3/wma/wav/amr格式 | |||
| * 视频(video):在上传视频素材时需要POST另一个表单,id为description,包含素材的描述信息,内容格式为JSON | |||
| * 缩略图(thumb):文档未说明 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html | |||
| * </pre> | |||
| * | |||
| * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} | |||
| * @param material 上传的素材, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterial} | |||
| */ | |||
| public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 上传永久图文素材 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html | |||
| * </pre> | |||
| * | |||
| * @param news 上传的图文消息, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialNews} | |||
| */ | |||
| public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 下载声音或者图片永久素材 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html | |||
| * </pre> | |||
| * | |||
| * @param media_id 永久素材的id | |||
| */ | |||
| public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取视频永久素材的信息和下载地址 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html | |||
| * </pre> | |||
| * | |||
| * @param media_id 永久素材的id | |||
| */ | |||
| public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取图文永久素材的信息 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html | |||
| * </pre> | |||
| * | |||
| * @param media_id 永久素材的id | |||
| */ | |||
| public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 更新图文永久素材 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/4/19a59cba020d506e767360ca1be29450.html | |||
| * </pre> | |||
| * | |||
| * @param wxMpMaterialArticleUpdate 用来更新图文素材的bean, 请看{@link me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate} | |||
| */ | |||
| public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 删除永久素材 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/5/e66f61c303db51a6c0f90f46b15af5f5.html | |||
| * </pre> | |||
| * | |||
| * @param media_id 永久素材的id | |||
| */ | |||
| public boolean materialDelete(String media_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取各类素材总数 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/16/8cc64f8c189674b421bee3ed403993b8.html | |||
| * </pre> | |||
| */ | |||
| public WxMpMaterialCountResult materialCount() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分页获取图文素材列表 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html | |||
| * </pre> | |||
| * | |||
| * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 | |||
| * @param count 返回素材的数量,取值在1到20之间 | |||
| */ | |||
| public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 分页获取其他媒体素材列表 | |||
| * | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html | |||
| * </pre> | |||
| * | |||
| * @param type 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} | |||
| * @param offset 从全部素材的该偏移位置开始返回,0表示从第一个素材 返回 | |||
| * @param count 返回素材的数量,取值在1到20之间 | |||
| */ | |||
| public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException; | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.bean.WxMenu; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| /** | |||
| * 菜单相关操作接口 | |||
| * | |||
| * @author Binary Wang | |||
| */ | |||
| public interface WxMpMenuService { | |||
| /** | |||
| * <pre> | |||
| * 自定义菜单创建接口 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口 | |||
| * 如果要创建个性化菜单,请设置matchrule属性 | |||
| * 详情请见:http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html | |||
| * </pre> | |||
| */ | |||
| public void menuCreate(WxMenu menu) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 自定义菜单删除接口 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单删除接口 | |||
| * </pre> | |||
| */ | |||
| public void menuDelete() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 删除个性化菜单接口 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html | |||
| * </pre> | |||
| * | |||
| * @param menuid | |||
| */ | |||
| public void menuDelete(String menuid) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 自定义菜单查询接口 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单查询接口 | |||
| * </pre> | |||
| */ | |||
| public WxMenu menuGet() throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 测试个性化菜单匹配结果 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html | |||
| * </pre> | |||
| * | |||
| * @param userid 可以是粉丝的OpenID,也可以是粉丝的微信号。 | |||
| */ | |||
| public WxMenu menuTryMatch(String userid) throws WxErrorException; | |||
| } | |||
| @@ -10,7 +10,6 @@ public interface WxMpMessageMatcher { | |||
| /** | |||
| * 消息是否匹配某种模式 | |||
| * @param message | |||
| * @return | |||
| */ | |||
| public boolean match(WxMpXmlMessage message); | |||
| @@ -124,7 +124,6 @@ public class WxMpMessageRouter { | |||
| /** | |||
| * 开始一个新的Route规则 | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule rule() { | |||
| return new WxMpMessageRouterRule(this); | |||
| @@ -34,9 +34,9 @@ public class WxMpMessageRouterRule { | |||
| private boolean reEnter = false; | |||
| private List<WxMpMessageHandler> handlers = new ArrayList<WxMpMessageHandler>(); | |||
| private List<WxMpMessageHandler> handlers = new ArrayList<>(); | |||
| private List<WxMpMessageInterceptor> interceptors = new ArrayList<WxMpMessageInterceptor>(); | |||
| private List<WxMpMessageInterceptor> interceptors = new ArrayList<>(); | |||
| public WxMpMessageRouterRule(WxMpMessageRouter routerBuilder) { | |||
| this.routerBuilder = routerBuilder; | |||
| @@ -44,9 +44,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 设置是否异步执行,默认是true | |||
| * | |||
| * @param async | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule async(boolean async) { | |||
| this.async = async; | |||
| @@ -55,9 +52,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果msgType等于某值 | |||
| * | |||
| * @param msgType | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule msgType(String msgType) { | |||
| this.msgType = msgType; | |||
| @@ -66,9 +60,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果event等于某值 | |||
| * | |||
| * @param event | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule event(String event) { | |||
| this.event = event; | |||
| @@ -77,9 +68,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果eventKey等于某值 | |||
| * | |||
| * @param eventKey | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule eventKey(String eventKey) { | |||
| this.eventKey = eventKey; | |||
| @@ -88,9 +76,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果content等于某值 | |||
| * | |||
| * @param content | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule content(String content) { | |||
| this.content = content; | |||
| @@ -99,9 +84,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果content匹配该正则表达式 | |||
| * | |||
| * @param regex | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule rContent(String regex) { | |||
| this.rContent = regex; | |||
| @@ -110,9 +92,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果fromUser等于某值 | |||
| * | |||
| * @param fromUser | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule fromUser(String fromUser) { | |||
| this.fromUser = fromUser; | |||
| @@ -121,9 +100,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候 | |||
| * | |||
| * @param matcher | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule matcher(WxMpMessageMatcher matcher) { | |||
| this.matcher = matcher; | |||
| @@ -132,9 +108,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 设置微信消息拦截器 | |||
| * | |||
| * @param interceptor | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule interceptor(WxMpMessageInterceptor interceptor) { | |||
| return interceptor(interceptor, (WxMpMessageInterceptor[]) null); | |||
| @@ -142,10 +115,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 设置微信消息拦截器 | |||
| * | |||
| * @param interceptor | |||
| * @param otherInterceptors | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule interceptor(WxMpMessageInterceptor interceptor, WxMpMessageInterceptor... otherInterceptors) { | |||
| this.interceptors.add(interceptor); | |||
| @@ -159,9 +128,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 设置微信消息处理器 | |||
| * | |||
| * @param handler | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule handler(WxMpMessageHandler handler) { | |||
| return handler(handler, (WxMpMessageHandler[]) null); | |||
| @@ -169,10 +135,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 设置微信消息处理器 | |||
| * | |||
| * @param handler | |||
| * @param otherHandlers | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouterRule handler(WxMpMessageHandler handler, WxMpMessageHandler... otherHandlers) { | |||
| this.handlers.add(handler); | |||
| @@ -186,8 +148,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 规则结束,代表如果一个消息匹配该规则,那么它将不再会进入其他规则 | |||
| * | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouter end() { | |||
| this.routerBuilder.getRules().add(this); | |||
| @@ -196,8 +156,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 规则结束,但是消息还会进入其他规则 | |||
| * | |||
| * @return | |||
| */ | |||
| public WxMpMessageRouter next() { | |||
| this.reEnter = true; | |||
| @@ -207,8 +165,6 @@ public class WxMpMessageRouterRule { | |||
| /** | |||
| * 将微信自定义的事件修正为不区分大小写, | |||
| * 比如框架定义的事件常量为click,但微信传递过来的却是CLICK | |||
| * @param wxMessage | |||
| * @return | |||
| */ | |||
| protected boolean test(WxMpXmlMessage wxMessage) { | |||
| return | |||
| @@ -243,7 +199,7 @@ public class WxMpMessageRouterRule { | |||
| try { | |||
| Map<String, Object> context = new HashMap<String, Object>(); | |||
| Map<String, Object> context = new HashMap<>(); | |||
| // 如果拦截器不通过 | |||
| for (WxMpMessageInterceptor interceptor : this.interceptors) { | |||
| if (!interceptor.intercept(wxMessage, context, wxMpService, sessionManager)) { | |||
| @@ -255,6 +211,9 @@ public class WxMpMessageRouterRule { | |||
| WxMpXmlOutMessage res = null; | |||
| for (WxMpMessageHandler handler : this.handlers) { | |||
| // 返回最后handler的结果 | |||
| if(handler == null){ | |||
| continue; | |||
| } | |||
| res = handler.handle(wxMessage, context, wxMpService, sessionManager); | |||
| } | |||
| return res; | |||
| @@ -266,11 +225,11 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public WxMpMessageRouter getRouterBuilder() { | |||
| return routerBuilder; | |||
| return this.routerBuilder; | |||
| } | |||
| public boolean isAsync() { | |||
| return async; | |||
| return this.async; | |||
| } | |||
| public void setAsync(boolean async) { | |||
| @@ -278,7 +237,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getFromUser() { | |||
| return fromUser; | |||
| return this.fromUser; | |||
| } | |||
| public void setFromUser(String fromUser) { | |||
| @@ -286,7 +245,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getMsgType() { | |||
| return msgType; | |||
| return this.msgType; | |||
| } | |||
| public void setMsgType(String msgType) { | |||
| @@ -294,7 +253,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getEvent() { | |||
| return event; | |||
| return this.event; | |||
| } | |||
| public void setEvent(String event) { | |||
| @@ -302,7 +261,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getEventKey() { | |||
| return eventKey; | |||
| return this.eventKey; | |||
| } | |||
| public void setEventKey(String eventKey) { | |||
| @@ -310,7 +269,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getContent() { | |||
| return content; | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| @@ -318,7 +277,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public String getrContent() { | |||
| return rContent; | |||
| return this.rContent; | |||
| } | |||
| public void setrContent(String rContent) { | |||
| @@ -326,7 +285,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public WxMpMessageMatcher getMatcher() { | |||
| return matcher; | |||
| return this.matcher; | |||
| } | |||
| public void setMatcher(WxMpMessageMatcher matcher) { | |||
| @@ -334,7 +293,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public boolean isReEnter() { | |||
| return reEnter; | |||
| return this.reEnter; | |||
| } | |||
| public void setReEnter(boolean reEnter) { | |||
| @@ -342,7 +301,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public List<WxMpMessageHandler> getHandlers() { | |||
| return handlers; | |||
| return this.handlers; | |||
| } | |||
| public void setHandlers(List<WxMpMessageHandler> handlers) { | |||
| @@ -350,7 +309,7 @@ public class WxMpMessageRouterRule { | |||
| } | |||
| public List<WxMpMessageInterceptor> getInterceptors() { | |||
| return interceptors; | |||
| return this.interceptors; | |||
| } | |||
| public void setInterceptors(List<WxMpMessageInterceptor> interceptors) { | |||
| @@ -0,0 +1,77 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; | |||
| import java.io.File; | |||
| /** | |||
| * 二维码相关操作接口 | |||
| * | |||
| * @author Binary Wang | |||
| */ | |||
| public interface WxMpQrcodeService { | |||
| /** | |||
| * <pre> | |||
| * 换取临时二维码ticket | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param scene_id 参数。 | |||
| * @param expire_seconds 过期秒数,默认60秒,最小60秒,最大1800秒 | |||
| */ | |||
| public WxMpQrCodeTicket qrCodeCreateTmpTicket(int scene_id, Integer expire_seconds) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 换取永久二维码ticket | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param scene_id 参数。永久二维码时最大值为100000(目前参数只支持1--100000) | |||
| */ | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(int scene_id) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 换取永久字符串二维码ticket | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param scene_str 参数。字符串类型长度现在为1到64 | |||
| */ | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 换取二维码图片文件,jpg格式 | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param ticket 二维码ticket | |||
| */ | |||
| public File qrCodePicture(WxMpQrCodeTicket ticket) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 换取二维码图片url地址(可以选择是否生成压缩的网址) | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param ticket 二维码ticket | |||
| * @param needShortUrl 是否需要压缩的二维码地址 | |||
| */ | |||
| public String qrCodePictureUrl(String ticket, boolean needShortUrl) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 换取二维码图片url地址 | |||
| * 详情请见: <a href="http://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html">生成带参数的二维码</a> | |||
| * </pre> | |||
| * | |||
| * @param ticket 二维码ticket | |||
| */ | |||
| public String qrCodePictureUrl(String ticket) throws WxErrorException; | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserCumulate; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserList; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserSummary; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| /** | |||
| * 用户管理和统计相关操作接口 | |||
| * | |||
| * @author Binary Wang | |||
| */ | |||
| public interface WxMpUserService { | |||
| /** | |||
| * <pre> | |||
| * 设置用户备注名接口 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=设置用户备注名接口 | |||
| * </pre> | |||
| * | |||
| * @param openid 用户openid | |||
| * @param remark 备注名 | |||
| */ | |||
| public void userUpdateRemark(String openid, String remark) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取用户基本信息 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取用户基本信息 | |||
| * </pre> | |||
| * | |||
| * @param openid 用户openid | |||
| * @param lang 语言,zh_CN 简体(默认),zh_TW 繁体,en 英语 | |||
| */ | |||
| public WxMpUser userInfo(String openid, String lang) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取关注者列表 | |||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取关注者列表 | |||
| * </pre> | |||
| * | |||
| * @param next_openid 可选,第一个拉取的OPENID,null为从头开始拉取 | |||
| */ | |||
| public WxMpUserList userList(String next_openid) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取用户增减数据 | |||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||
| * </pre> | |||
| * | |||
| * @param beginDate 最大时间跨度7天 | |||
| * @param endDate endDate不能早于begingDate | |||
| */ | |||
| List<WxMpUserSummary> dataCubeUserSummary(Date beginDate, Date endDate) throws WxErrorException; | |||
| /** | |||
| * <pre> | |||
| * 获取累计用户数据 | |||
| * http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html | |||
| * </pre> | |||
| * | |||
| * @param beginDate 最大时间跨度7天 | |||
| * @param endDate endDate不能早于begingDate | |||
| */ | |||
| List<WxMpUserCumulate> dataCubeUserCumulate(Date beginDate, Date endDate) throws WxErrorException; | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.gson.JsonElement; | |||
| import com.google.gson.JsonObject; | |||
| import com.google.gson.internal.Streams; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import com.google.gson.stream.JsonReader; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||
| import me.chanjar.weixin.mp.api.WxMpGroupService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpGroup; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import java.io.StringReader; | |||
| import java.util.List; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| */ | |||
| public class WxMpGroupServiceImpl implements WxMpGroupService { | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/groups"; | |||
| private WxMpService wxMpService; | |||
| public WxMpGroupServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public WxMpGroup groupCreate(String name) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/create"; | |||
| JsonObject json = new JsonObject(); | |||
| JsonObject groupJson = new JsonObject(); | |||
| json.add("group", groupJson); | |||
| groupJson.addProperty("name", name); | |||
| String responseContent = this.wxMpService.execute( | |||
| new SimplePostRequestExecutor(), | |||
| url, | |||
| json.toString()); | |||
| return WxMpGroup.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public List<WxMpGroup> groupGet() throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/get"; | |||
| String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||
| /* | |||
| * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } | |||
| * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } | |||
| */ | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("groups"), | |||
| new TypeToken<List<WxMpGroup>>() { | |||
| }.getType()); | |||
| } | |||
| @Override | |||
| public long userGetGroup(String openid) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/getid"; | |||
| JsonObject o = new JsonObject(); | |||
| o.addProperty("openid", openid); | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, o.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("groupid")); | |||
| } | |||
| @Override | |||
| public void groupUpdate(WxMpGroup group) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/update"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, group.toJson()); | |||
| } | |||
| @Override | |||
| public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/members/update"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("openid", openid); | |||
| json.addProperty("to_groupid", to_groupid); | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,182 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import java.io.File; | |||
| import java.util.Date; | |||
| import com.google.gson.JsonObject; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.mp.api.WxMpKefuService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfAccountRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.request.WxMpKfSessionRequest; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfMsgList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfOnlineList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionGetResult; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionList; | |||
| import me.chanjar.weixin.mp.bean.kefu.result.WxMpKfSessionWaitCaseList; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKefuServiceImpl implements WxMpKefuService { | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/customservice"; | |||
| private WxMpService wxMpService; | |||
| public WxMpKefuServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public WxMpKfList kfList() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpKfList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfOnlineList kfOnlineList() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpKfOnlineList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public boolean kfAccountAdd(WxMpKfAccountRequest request) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/add"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountUpdate(WxMpKfAccountRequest request) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/update"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountInviteWorker(WxMpKfAccountRequest request) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/inviteworker"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountUploadHeadImg(String kfAccount, File imgFile) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/uploadheadimg?kf_account=" + kfAccount; | |||
| this.wxMpService.execute(new MediaUploadRequestExecutor(), url, imgFile); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfAccountDel(String kfAccount) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfaccount/del?kf_account=" + kfAccount; | |||
| this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfSessionCreate(String openid, String kfAccount) | |||
| throws WxErrorException { | |||
| WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); | |||
| String url = API_URL_PREFIX + "/kfsession/create"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public boolean kfSessionClose(String openid, String kfAccount) | |||
| throws WxErrorException { | |||
| WxMpKfSessionRequest request = new WxMpKfSessionRequest(kfAccount, openid); | |||
| String url = API_URL_PREFIX + "/kfsession/close"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, | |||
| request.toJson()); | |||
| return true; | |||
| } | |||
| @Override | |||
| public WxMpKfSessionGetResult kfSessionGet(String openid) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getsession?openid=" + openid; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpKfSessionGetResult.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfSessionList kfSessionList(String kfAccount) | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getsessionlist?kf_account=" + kfAccount; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpKfSessionList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfSessionWaitCaseList kfSessionGetWaitCase() | |||
| throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/kfsession/getwaitcase"; | |||
| String responseContent = this.wxMpService | |||
| .execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpKfSessionWaitCaseList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfMsgList kfMsgList(Date startTime, Date endTime, Long msgId, Integer number) throws WxErrorException { | |||
| if(number > 10000){ | |||
| throw new WxErrorException(WxError.newBuilder().setErrorMsg("非法参数请求,每次最多查询10000条记录!").build()); | |||
| } | |||
| if(startTime.after(endTime)){ | |||
| throw new WxErrorException(WxError.newBuilder().setErrorMsg("起始时间不能晚于结束时间!").build()); | |||
| } | |||
| String url = API_URL_PREFIX + "/msgrecord/getmsglist"; | |||
| JsonObject param = new JsonObject(); | |||
| param.addProperty("starttime", startTime.getTime() / 1000); //starttime 起始时间,unix时间戳 | |||
| param.addProperty("endtime", endTime.getTime() / 1000); //endtime 结束时间,unix时间戳,每次查询时段不能超过24小时 | |||
| param.addProperty("msgid", msgId); //msgid 消息id顺序从小到大,从1开始 | |||
| param.addProperty("number", number); //number 每次获取条数,最多10000条 | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, param.toString()); | |||
| return WxMpKfMsgList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKfMsgList kfMsgList(Date startTime, Date endTime) throws WxErrorException { | |||
| int number = 10000; | |||
| WxMpKfMsgList result = this.kfMsgList(startTime,endTime, 1L, number); | |||
| if(result != null && result.getNumber() == number){ | |||
| Long msgId = result.getMsgId(); | |||
| WxMpKfMsgList followingResult = this.kfMsgList(startTime,endTime, msgId, number); | |||
| while(followingResult != null && followingResult.getRecords().size() > 0){ | |||
| result.getRecords().addAll(followingResult.getRecords()); | |||
| result.setNumber(result.getNumber() + followingResult.getNumber()); | |||
| result.setMsgId(followingResult.getMsgId()); | |||
| followingResult = this.kfMsgList(startTime,endTime, followingResult.getMsgId(), number); | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| @@ -0,0 +1,158 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||
| import me.chanjar.weixin.common.util.http.MediaDownloadRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import me.chanjar.weixin.mp.api.WxMpMaterialService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.WxMpMaterial; | |||
| import me.chanjar.weixin.mp.bean.WxMpMaterialArticleUpdate; | |||
| import me.chanjar.weixin.mp.bean.WxMpMaterialNews; | |||
| import me.chanjar.weixin.mp.bean.result.*; | |||
| import me.chanjar.weixin.mp.util.http.*; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import java.util.UUID; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| */ | |||
| public class WxMpMaterialServiceImpl implements WxMpMaterialService { | |||
| private static final String MEDIA_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/media"; | |||
| private static final String MATERIAL_API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/material"; | |||
| private WxMpService wxMpService; | |||
| public WxMpMaterialServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { | |||
| return this.mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | |||
| } | |||
| @Override | |||
| public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { | |||
| String url = MEDIA_API_URL_PREFIX + "/upload?type=" + mediaType; | |||
| return this.wxMpService.execute(new MediaUploadRequestExecutor(), url, file); | |||
| } | |||
| @Override | |||
| public File mediaDownload(String media_id) throws WxErrorException { | |||
| String url = MEDIA_API_URL_PREFIX + "/get"; | |||
| return this.wxMpService.execute(new MediaDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, "media_id=" + media_id); | |||
| } | |||
| @Override | |||
| public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException { | |||
| String url = MEDIA_API_URL_PREFIX + "/uploadimg"; | |||
| return this.wxMpService.execute(new MediaImgUploadRequestExecutor(), url, file); | |||
| } | |||
| @Override | |||
| public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/add_material?type=" + mediaType; | |||
| return this.wxMpService.execute(new MaterialUploadRequestExecutor(), url, material); | |||
| } | |||
| @Override | |||
| public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException { | |||
| if (news == null || news.isEmpty()) { | |||
| throw new IllegalArgumentException("news is empty!"); | |||
| } | |||
| String url = MATERIAL_API_URL_PREFIX + "/add_news"; | |||
| String responseContent = this.wxMpService.post(url, news.toJson()); | |||
| return WxMpMaterialUploadResult.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/get_material"; | |||
| return this.wxMpService.execute(new MaterialVoiceAndImageDownloadRequestExecutor(this.wxMpService.getWxMpConfigStorage().getTmpDirFile()), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/get_material"; | |||
| return this.wxMpService.execute(new MaterialVideoInfoRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/get_material"; | |||
| return this.wxMpService.execute(new MaterialNewsInfoRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/update_news"; | |||
| String responseText = this.wxMpService.post(url, wxMpMaterialArticleUpdate.toJson()); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return true; | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean materialDelete(String media_id) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/del_material"; | |||
| return this.wxMpService.execute(new MaterialDeleteRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialCountResult materialCount() throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/get_materialcount"; | |||
| String responseText = this.wxMpService.get(url, null); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; | |||
| Map<String, Object> params = new HashMap<>(); | |||
| params.put("type", WxConsts.MATERIAL_NEWS); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException { | |||
| String url = MATERIAL_API_URL_PREFIX + "/batchget_material"; | |||
| Map<String, Object> params = new HashMap<>(); | |||
| params.put("type", type); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = this.wxMpService.post(url, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,74 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import me.chanjar.weixin.common.bean.WxMenu; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.mp.api.WxMpMenuService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| */ | |||
| public class WxMpMenuServiceImpl implements WxMpMenuService { | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/menu"; | |||
| private WxMpService wxMpService; | |||
| public WxMpMenuServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public void menuCreate(WxMenu menu) throws WxErrorException { | |||
| if (menu.getMatchRule() != null) { | |||
| String url = API_URL_PREFIX + "/addconditional"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, menu.toJson()); | |||
| } else { | |||
| String url = API_URL_PREFIX + "/create"; | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, menu.toJson()); | |||
| } | |||
| } | |||
| @Override | |||
| public void menuDelete() throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/delete"; | |||
| this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||
| } | |||
| @Override | |||
| public void menuDelete(String menuid) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/delconditional"; | |||
| this.wxMpService.execute(new SimpleGetRequestExecutor(), url, "menuid=" + menuid); | |||
| } | |||
| @Override | |||
| public WxMenu menuGet() throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/get"; | |||
| try { | |||
| String resultContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMenu.fromJson(resultContent); | |||
| } catch (WxErrorException e) { | |||
| // 46003 不存在的菜单数据 | |||
| if (e.getError().getErrorCode() == 46003) { | |||
| return null; | |||
| } | |||
| throw e; | |||
| } | |||
| } | |||
| @Override | |||
| public WxMenu menuTryMatch(String userid) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/trymatch"; | |||
| try { | |||
| String resultContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, "user_id=" + userid); | |||
| return WxMenu.fromJson(resultContent); | |||
| } catch (WxErrorException e) { | |||
| // 46003 不存在的菜单数据 46002 不存在的菜单版本 | |||
| if (e.getError().getErrorCode() == 46003 || e.getError().getErrorCode() == 46002) { | |||
| return null; | |||
| } | |||
| throw e; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,102 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.gson.JsonObject; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.mp.api.WxMpQrcodeService; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; | |||
| import me.chanjar.weixin.mp.util.http.QrCodeRequestExecutor; | |||
| import java.io.File; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLEncoder; | |||
| import java.nio.charset.StandardCharsets; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| */ | |||
| public class WxMpQrcodeServiceImpl implements WxMpQrcodeService { | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/qrcode"; | |||
| private WxMpService wxMpService; | |||
| public WxMpQrcodeServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateTmpTicket(int scene_id, Integer expire_seconds) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_SCENE"); | |||
| if (expire_seconds != null) { | |||
| json.addProperty("expire_seconds", expire_seconds); | |||
| } | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_id", scene_id); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(int scene_id) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_LIMIT_SCENE"); | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_id", scene_id); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_LIMIT_STR_SCENE"); | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_str", scene_str); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = this.wxMpService.execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public File qrCodePicture(WxMpQrCodeTicket ticket) throws WxErrorException { | |||
| String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode"; | |||
| return this.wxMpService.execute(new QrCodeRequestExecutor(), url, ticket); | |||
| } | |||
| @Override | |||
| public String qrCodePictureUrl(String ticket, boolean needShortUrl) throws WxErrorException { | |||
| String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"; | |||
| try { | |||
| String resultUrl = String.format(url, | |||
| URLEncoder.encode(ticket, StandardCharsets.UTF_8.name())); | |||
| if (needShortUrl) { | |||
| return this.wxMpService.shortUrl(resultUrl); | |||
| } | |||
| return resultUrl; | |||
| } catch (UnsupportedEncodingException e) { | |||
| WxError error = WxError.newBuilder().setErrorCode(-1) | |||
| .setErrorMsg(e.getMessage()).build(); | |||
| throw new WxErrorException(error); | |||
| } | |||
| } | |||
| @Override | |||
| public String qrCodePictureUrl(String ticket) throws WxErrorException { | |||
| return qrCodePictureUrl(ticket, false); | |||
| } | |||
| } | |||
| @@ -1,34 +1,26 @@ | |||
| package me.chanjar.weixin.mp.api; | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.gson.*; | |||
| import com.google.gson.internal.Streams; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import com.google.gson.stream.JsonReader; | |||
| import com.thoughtworks.xstream.XStream; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.common.bean.WxAccessToken; | |||
| import me.chanjar.weixin.common.bean.WxCardApiSignature; | |||
| import me.chanjar.weixin.common.bean.WxJsapiSignature; | |||
| import me.chanjar.weixin.common.bean.WxMenu; | |||
| import me.chanjar.weixin.common.bean.result.WxError; | |||
| import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.session.StandardSessionManager; | |||
| import me.chanjar.weixin.common.session.WxSessionManager; | |||
| import me.chanjar.weixin.common.util.RandomUtils; | |||
| import me.chanjar.weixin.common.util.crypto.SHA1; | |||
| import me.chanjar.weixin.common.util.crypto.WxCryptUtil; | |||
| import me.chanjar.weixin.common.util.fs.FileUtils; | |||
| import me.chanjar.weixin.common.util.http.*; | |||
| import me.chanjar.weixin.common.util.json.GsonHelper; | |||
| import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
| import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||
| import me.chanjar.weixin.mp.api.*; | |||
| import me.chanjar.weixin.mp.bean.*; | |||
| import me.chanjar.weixin.mp.bean.result.*; | |||
| import me.chanjar.weixin.mp.util.http.*; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import org.apache.commons.io.Charsets; | |||
| import org.apache.http.Consts; | |||
| import org.apache.http.HttpHost; | |||
| import org.apache.http.client.ClientProtocolException; | |||
| @@ -44,12 +36,8 @@ import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| import org.slf4j.helpers.MessageFormatter; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.StringReader; | |||
| import java.io.UnsupportedEncodingException; | |||
| import java.net.URLEncoder; | |||
| import java.security.NoSuchAlgorithmException; | |||
| import java.util.*; | |||
| import java.util.Map.Entry; | |||
| @@ -74,6 +62,18 @@ public class WxMpServiceImpl implements WxMpService { | |||
| protected final Object globalCardApiTicketRefreshLock = new Object(); | |||
| protected WxMpConfigStorage wxMpConfigStorage; | |||
| protected WxMpKefuService kefuService = new WxMpKefuServiceImpl(this); | |||
| protected WxMpMaterialService materialService = new WxMpMaterialServiceImpl(this); | |||
| protected WxMpMenuService menuService = new WxMpMenuServiceImpl(this); | |||
| protected WxMpUserService userService = new WxMpUserServiceImpl(this); | |||
| protected WxMpGroupService groupService = new WxMpGroupServiceImpl(this); | |||
| protected WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); | |||
| protected CloseableHttpClient httpClient; | |||
| @@ -195,172 +195,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| execute(new SimplePostRequestExecutor(), url, message.toJson()); | |||
| } | |||
| @Override | |||
| public void menuCreate(WxMenu menu) throws WxErrorException { | |||
| if (menu.getMatchRule() != null) { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/addconditional"; | |||
| execute(new SimplePostRequestExecutor(), url, menu.toJson()); | |||
| } else { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/create"; | |||
| execute(new SimplePostRequestExecutor(), url, menu.toJson()); | |||
| } | |||
| } | |||
| @Override | |||
| public void menuDelete() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/delete"; | |||
| execute(new SimpleGetRequestExecutor(), url, null); | |||
| } | |||
| @Override | |||
| public void menuDelete(String menuid) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/delconditional"; | |||
| execute(new SimpleGetRequestExecutor(), url, "menuid=" + menuid); | |||
| } | |||
| @Override | |||
| public WxMenu menuGet() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/get"; | |||
| try { | |||
| String resultContent = execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMenu.fromJson(resultContent); | |||
| } catch (WxErrorException e) { | |||
| // 46003 不存在的菜单数据 | |||
| if (e.getError().getErrorCode() == 46003) { | |||
| return null; | |||
| } | |||
| throw e; | |||
| } | |||
| } | |||
| @Override | |||
| public WxMenu menuTryMatch(String userid) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/menu/trymatch"; | |||
| try { | |||
| String resultContent = execute(new SimpleGetRequestExecutor(), url, "user_id=" + userid); | |||
| return WxMenu.fromJson(resultContent); | |||
| } catch (WxErrorException e) { | |||
| // 46003 不存在的菜单数据 46002 不存在的菜单版本 | |||
| if (e.getError().getErrorCode() == 46003 || e.getError().getErrorCode() == 46002) { | |||
| return null; | |||
| } | |||
| throw e; | |||
| } | |||
| } | |||
| @Override | |||
| public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) throws WxErrorException, IOException { | |||
| return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType)); | |||
| } | |||
| @Override | |||
| public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/media/upload?type=" + mediaType; | |||
| return execute(new MediaUploadRequestExecutor(), url, file); | |||
| } | |||
| @Override | |||
| public File mediaDownload(String media_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/media/get"; | |||
| return execute(new MediaDownloadRequestExecutor(this.wxMpConfigStorage.getTmpDirFile()), url, "media_id=" + media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialUploadResult materialFileUpload(String mediaType, WxMpMaterial material) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/add_material?type=" + mediaType; | |||
| return execute(new MaterialUploadRequestExecutor(), url, material); | |||
| } | |||
| @Override | |||
| public WxMpMaterialUploadResult materialNewsUpload(WxMpMaterialNews news) throws WxErrorException { | |||
| if (news == null || news.isEmpty()) { | |||
| throw new IllegalArgumentException("news is empty!"); | |||
| } | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/add_news"; | |||
| String responseContent = post(url, news.toJson()); | |||
| return WxMpMaterialUploadResult.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public InputStream materialImageOrVoiceDownload(String media_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/get_material"; | |||
| return execute(new MaterialVoiceAndImageDownloadRequestExecutor(this.wxMpConfigStorage.getTmpDirFile()), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialVideoInfoResult materialVideoInfo(String media_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/get_material"; | |||
| return execute(new MaterialVideoInfoRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialNews materialNewsInfo(String media_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/get_material"; | |||
| return execute(new MaterialNewsInfoRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public boolean materialNewsUpdate(WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/update_news"; | |||
| String responseText = post(url, wxMpMaterialArticleUpdate.toJson()); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return true; | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean materialDelete(String media_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/del_material"; | |||
| return execute(new MaterialDeleteRequestExecutor(), url, media_id); | |||
| } | |||
| @Override | |||
| public WxMpMaterialCountResult materialCount() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/get_materialcount"; | |||
| String responseText = get(url, null); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialCountResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMpMaterialNewsBatchGetResult materialNewsBatchGet(int offset, int count) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"; | |||
| Map<String, Object> params = new HashMap<>(); | |||
| params.put("type", WxConsts.MATERIAL_NEWS); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = post(url, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialNewsBatchGetResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/material/batchget_material"; | |||
| Map<String, Object> params = new HashMap<>(); | |||
| params.put("type", type); | |||
| params.put("offset", offset); | |||
| params.put("count", count); | |||
| String responseText = post(url, WxGsonBuilder.create().toJson(params)); | |||
| WxError wxError = WxError.fromJson(responseText); | |||
| if (wxError.getErrorCode() == 0) { | |||
| return WxMpGsonBuilder.create().fromJson(responseText, WxMpMaterialFileBatchGetResult.class); | |||
| } else { | |||
| throw new WxErrorException(wxError); | |||
| } | |||
| } | |||
| @Override | |||
| public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews"; | |||
| @@ -389,158 +223,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return WxMpMassSendResult.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpGroup groupCreate(String name) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/groups/create"; | |||
| JsonObject json = new JsonObject(); | |||
| JsonObject groupJson = new JsonObject(); | |||
| json.add("group", groupJson); | |||
| groupJson.addProperty("name", name); | |||
| String responseContent = execute( | |||
| new SimplePostRequestExecutor(), | |||
| url, | |||
| json.toString()); | |||
| return WxMpGroup.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public List<WxMpGroup> groupGet() throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/groups/get"; | |||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | |||
| /* | |||
| * 操蛋的微信API,创建时返回的是 { group : { id : ..., name : ...} } | |||
| * 查询时返回的是 { groups : [ { id : ..., name : ..., count : ... }, ... ] } | |||
| */ | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("groups"), | |||
| new TypeToken<List<WxMpGroup>>() { | |||
| }.getType()); | |||
| } | |||
| @Override | |||
| public long userGetGroup(String openid) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/groups/getid"; | |||
| JsonObject o = new JsonObject(); | |||
| o.addProperty("openid", openid); | |||
| String responseContent = execute(new SimplePostRequestExecutor(), url, o.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("groupid")); | |||
| } | |||
| @Override | |||
| public void groupUpdate(WxMpGroup group) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/groups/update"; | |||
| execute(new SimplePostRequestExecutor(), url, group.toJson()); | |||
| } | |||
| @Override | |||
| public void userUpdateGroup(String openid, long to_groupid) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/groups/members/update"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("openid", openid); | |||
| json.addProperty("to_groupid", to_groupid); | |||
| execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| } | |||
| @Override | |||
| public void userUpdateRemark(String openid, String remark) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("openid", openid); | |||
| json.addProperty("remark", remark); | |||
| execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| } | |||
| @Override | |||
| public WxMpUser userInfo(String openid, String lang) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/user/info"; | |||
| lang = lang == null ? "zh_CN" : lang; | |||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); | |||
| return WxMpUser.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpUserList userList(String next_openid) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/user/get"; | |||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); | |||
| return WxMpUserList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateTmpTicket(int scene_id, Integer expire_seconds) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_SCENE"); | |||
| if (expire_seconds != null) { | |||
| json.addProperty("expire_seconds", expire_seconds); | |||
| } | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_id", scene_id); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(int scene_id) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_LIMIT_SCENE"); | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_id", scene_id); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpQrCodeTicket qrCodeCreateLastTicket(String scene_str) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("action_name", "QR_LIMIT_STR_SCENE"); | |||
| JsonObject actionInfo = new JsonObject(); | |||
| JsonObject scene = new JsonObject(); | |||
| scene.addProperty("scene_str", scene_str); | |||
| actionInfo.add("scene", scene); | |||
| json.add("action_info", actionInfo); | |||
| String responseContent = execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| return WxMpQrCodeTicket.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public File qrCodePicture(WxMpQrCodeTicket ticket) throws WxErrorException { | |||
| String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode"; | |||
| return execute(new QrCodeRequestExecutor(), url, ticket); | |||
| } | |||
| @Override | |||
| public String qrCodePictureUrl(String ticket, boolean needShortUrl) throws WxErrorException { | |||
| String url = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s"; | |||
| try { | |||
| String resultUrl = String.format(url, | |||
| URLEncoder.encode(ticket, Charsets.UTF_8.name())); | |||
| if(needShortUrl){ | |||
| return this.shortUrl(resultUrl); | |||
| } | |||
| return resultUrl; | |||
| } catch (UnsupportedEncodingException e) { | |||
| WxError error = WxError.newBuilder().setErrorCode(-1) | |||
| .setErrorMsg(e.getMessage()).build(); | |||
| throw new WxErrorException(error); | |||
| } | |||
| } | |||
| @Override | |||
| public String qrCodePictureUrl(String ticket) throws WxErrorException { | |||
| return qrCodePictureUrl(ticket, false); | |||
| } | |||
| @Override | |||
| public String shortUrl(String long_url) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/shorturl"; | |||
| @@ -685,33 +367,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return ipArray; | |||
| } | |||
| @Override | |||
| public List<WxMpUserSummary> getUserSummary(Date beginDate, Date endDate) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/datacube/getusersummary"; | |||
| JsonObject param = new JsonObject(); | |||
| param.addProperty("begin_date", SIMPLE_DATE_FORMAT.format(beginDate)); | |||
| param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate)); | |||
| String responseContent = post(url, param.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), | |||
| new TypeToken<List<WxMpUserSummary>>() { | |||
| }.getType()); | |||
| } | |||
| @Override | |||
| public List<WxMpUserCumulate> getUserCumulate(Date beginDate, Date endDate) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/datacube/getusercumulate"; | |||
| JsonObject param = new JsonObject(); | |||
| param.addProperty("begin_date", SIMPLE_DATE_FORMAT.format(beginDate)); | |||
| param.addProperty("end_date", SIMPLE_DATE_FORMAT.format(endDate)); | |||
| String responseContent = post(url, param.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), | |||
| new TypeToken<List<WxMpUserCumulate>>() { | |||
| }.getType()); | |||
| } | |||
| @Override | |||
| public String get(String url, String queryParam) throws WxErrorException { | |||
| return execute(new SimpleGetRequestExecutor(), url, queryParam); | |||
| @@ -724,12 +379,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| /** | |||
| * 向微信端发送请求,在这里执行的策略是当发生access_token过期时才去刷新,然后重新执行请求,而不是全局定时请求 | |||
| * | |||
| * @param executor | |||
| * @param uri | |||
| * @param data | |||
| * @return | |||
| * @throws WxErrorException | |||
| */ | |||
| @Override | |||
| public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | |||
| @@ -822,6 +471,11 @@ public class WxMpServiceImpl implements WxMpService { | |||
| this.httpClient = apacheHttpClientBuilder.build(); | |||
| } | |||
| @Override | |||
| public WxMpConfigStorage getWxMpConfigStorage() { | |||
| return this.wxMpConfigStorage; | |||
| } | |||
| @Override | |||
| public void setRetrySleepMillis(int retrySleepMillis) { | |||
| this.retrySleepMillis = retrySleepMillis; | |||
| @@ -1347,12 +1001,6 @@ public class WxMpServiceImpl implements WxMpService { | |||
| return WxMpMassSendResult.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMediaImgUploadResult mediaImgUpload(File file) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/cgi-bin/media/uploadimg"; | |||
| return execute(new MediaImgUploadRequestExecutor(), url, file); | |||
| } | |||
| @Override | |||
| public String setIndustry(WxMpIndustry wxMpIndustry) throws WxErrorException { | |||
| if (null == wxMpIndustry.getPrimaryIndustry() || null == wxMpIndustry.getPrimaryIndustry().getId() | |||
| @@ -1369,4 +1017,35 @@ public class WxMpServiceImpl implements WxMpService { | |||
| String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | |||
| return WxMpIndustry.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpKefuService getKefuService() { | |||
| return this.kefuService; | |||
| } | |||
| @Override | |||
| public WxMpMaterialService getMaterialService() { | |||
| return this.materialService; | |||
| } | |||
| @Override | |||
| public WxMpMenuService getMenuService() { | |||
| return this.menuService; | |||
| } | |||
| @Override | |||
| public WxMpUserService getUserService() { | |||
| return this.userService; | |||
| } | |||
| @Override | |||
| public WxMpGroupService getGroupService() { | |||
| return this.groupService; | |||
| } | |||
| @Override | |||
| public WxMpQrcodeService getQrcodeService() { | |||
| return this.qrCodeService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,83 @@ | |||
| package me.chanjar.weixin.mp.api.impl; | |||
| import com.google.gson.JsonElement; | |||
| import com.google.gson.JsonObject; | |||
| import com.google.gson.internal.Streams; | |||
| import com.google.gson.reflect.TypeToken; | |||
| import com.google.gson.stream.JsonReader; | |||
| import me.chanjar.weixin.common.exception.WxErrorException; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.mp.api.WxMpService; | |||
| import me.chanjar.weixin.mp.api.WxMpUserService; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserCumulate; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserList; | |||
| import me.chanjar.weixin.mp.bean.result.WxMpUserSummary; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import java.io.StringReader; | |||
| import java.util.Date; | |||
| import java.util.List; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/21. | |||
| */ | |||
| public class WxMpUserServiceImpl implements WxMpUserService { | |||
| private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/user"; | |||
| private WxMpService wxMpService; | |||
| public WxMpUserServiceImpl(WxMpService wxMpService) { | |||
| this.wxMpService = wxMpService; | |||
| } | |||
| @Override | |||
| public void userUpdateRemark(String openid, String remark) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/info/updateremark"; | |||
| JsonObject json = new JsonObject(); | |||
| json.addProperty("openid", openid); | |||
| json.addProperty("remark", remark); | |||
| this.wxMpService.execute(new SimplePostRequestExecutor(), url, json.toString()); | |||
| } | |||
| @Override | |||
| public WxMpUser userInfo(String openid, String lang) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/info"; | |||
| lang = lang == null ? "zh_CN" : lang; | |||
| String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, "openid=" + openid + "&lang=" + lang); | |||
| return WxMpUser.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public WxMpUserList userList(String next_openid) throws WxErrorException { | |||
| String url = API_URL_PREFIX + "/get"; | |||
| String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, next_openid == null ? null : "next_openid=" + next_openid); | |||
| return WxMpUserList.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public List<WxMpUserSummary> dataCubeUserSummary(Date beginDate, Date endDate) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/datacube/getusersummary"; | |||
| JsonObject param = new JsonObject(); | |||
| param.addProperty("begin_date", WxMpService.SIMPLE_DATE_FORMAT.format(beginDate)); | |||
| param.addProperty("end_date", WxMpService.SIMPLE_DATE_FORMAT.format(endDate)); | |||
| String responseContent = this.wxMpService.post(url, param.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), | |||
| new TypeToken<List<WxMpUserSummary>>() { | |||
| }.getType()); | |||
| } | |||
| @Override | |||
| public List<WxMpUserCumulate> dataCubeUserCumulate(Date beginDate, Date endDate) throws WxErrorException { | |||
| String url = "https://api.weixin.qq.com/datacube/getusercumulate"; | |||
| JsonObject param = new JsonObject(); | |||
| param.addProperty("begin_date", WxMpService.SIMPLE_DATE_FORMAT.format(beginDate)); | |||
| param.addProperty("end_date", WxMpService.SIMPLE_DATE_FORMAT.format(endDate)); | |||
| String responseContent = this.wxMpService.post(url, param.toString()); | |||
| JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent))); | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement.getAsJsonObject().get("list"), | |||
| new TypeToken<List<WxMpUserCumulate>>() { | |||
| }.getType()); | |||
| } | |||
| } | |||
| @@ -13,7 +13,8 @@ import java.util.List; | |||
| * | |||
| */ | |||
| public class WxMpCustomMessage implements Serializable { | |||
| private static final long serialVersionUID = -9196732086954365246L; | |||
| private String toUser; | |||
| private String msgType; | |||
| private String content; | |||
| @@ -23,16 +24,17 @@ public class WxMpCustomMessage implements Serializable { | |||
| private String description; | |||
| private String musicUrl; | |||
| private String hqMusicUrl; | |||
| private List<WxArticle> articles = new ArrayList<WxArticle>(); | |||
| private String kfAccount; | |||
| private List<WxArticle> articles = new ArrayList<>(); | |||
| public String getToUser() { | |||
| return toUser; | |||
| return this.toUser; | |||
| } | |||
| public void setToUser(String toUser) { | |||
| this.toUser = toUser; | |||
| } | |||
| public String getMsgType() { | |||
| return msgType; | |||
| return this.msgType; | |||
| } | |||
| /** | |||
| @@ -51,49 +53,49 @@ public class WxMpCustomMessage implements Serializable { | |||
| this.msgType = msgType; | |||
| } | |||
| public String getContent() { | |||
| return content; | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| this.content = content; | |||
| } | |||
| public String getMediaId() { | |||
| return mediaId; | |||
| return this.mediaId; | |||
| } | |||
| public void setMediaId(String mediaId) { | |||
| this.mediaId = mediaId; | |||
| } | |||
| public String getThumbMediaId() { | |||
| return thumbMediaId; | |||
| return this.thumbMediaId; | |||
| } | |||
| public void setThumbMediaId(String thumbMediaId) { | |||
| this.thumbMediaId = thumbMediaId; | |||
| } | |||
| public String getTitle() { | |||
| return title; | |||
| return this.title; | |||
| } | |||
| public void setTitle(String title) { | |||
| this.title = title; | |||
| } | |||
| public String getDescription() { | |||
| return description; | |||
| return this.description; | |||
| } | |||
| public void setDescription(String description) { | |||
| this.description = description; | |||
| } | |||
| public String getMusicUrl() { | |||
| return musicUrl; | |||
| return this.musicUrl; | |||
| } | |||
| public void setMusicUrl(String musicUrl) { | |||
| this.musicUrl = musicUrl; | |||
| } | |||
| public String getHqMusicUrl() { | |||
| return hqMusicUrl; | |||
| return this.hqMusicUrl; | |||
| } | |||
| public void setHqMusicUrl(String hqMusicUrl) { | |||
| this.hqMusicUrl = hqMusicUrl; | |||
| } | |||
| public List<WxArticle> getArticles() { | |||
| return articles; | |||
| return this.articles; | |||
| } | |||
| public void setArticles(List<WxArticle> articles) { | |||
| this.articles = articles; | |||
| @@ -111,25 +113,25 @@ public class WxMpCustomMessage implements Serializable { | |||
| private String picUrl; | |||
| public String getTitle() { | |||
| return title; | |||
| return this.title; | |||
| } | |||
| public void setTitle(String title) { | |||
| this.title = title; | |||
| } | |||
| public String getDescription() { | |||
| return description; | |||
| return this.description; | |||
| } | |||
| public void setDescription(String description) { | |||
| this.description = description; | |||
| } | |||
| public String getUrl() { | |||
| return url; | |||
| return this.url; | |||
| } | |||
| public void setUrl(String url) { | |||
| this.url = url; | |||
| } | |||
| public String getPicUrl() { | |||
| return picUrl; | |||
| return this.picUrl; | |||
| } | |||
| public void setPicUrl(String picUrl) { | |||
| this.picUrl = picUrl; | |||
| @@ -139,7 +141,6 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得文本消息builder | |||
| * @return | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| @@ -147,7 +148,6 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得图片消息builder | |||
| * @return | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| @@ -155,7 +155,6 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得语音消息builder | |||
| * @return | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| @@ -163,7 +162,6 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得视频消息builder | |||
| * @return | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| @@ -171,7 +169,6 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得音乐消息builder | |||
| * @return | |||
| */ | |||
| public static MusicBuilder MUSIC() { | |||
| return new MusicBuilder(); | |||
| @@ -179,10 +176,17 @@ public class WxMpCustomMessage implements Serializable { | |||
| /** | |||
| * 获得图文消息builder | |||
| * @return | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| } | |||
| public String getKfAccount() { | |||
| return kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| } | |||
| @@ -12,8 +12,9 @@ import java.util.List; | |||
| * @author chanjarster | |||
| */ | |||
| public class WxMpMassOpenIdsMessage implements Serializable { | |||
| private static final long serialVersionUID = -8022910911104788999L; | |||
| private List<String> toUsers = new ArrayList<String>(); | |||
| private List<String> toUsers = new ArrayList<>(); | |||
| private String msgType; | |||
| private String content; | |||
| private String mediaId; | |||
| @@ -23,7 +24,7 @@ public class WxMpMassOpenIdsMessage implements Serializable { | |||
| } | |||
| public String getMsgType() { | |||
| return msgType; | |||
| return this.msgType; | |||
| } | |||
| /** | |||
| @@ -43,7 +44,7 @@ public class WxMpMassOpenIdsMessage implements Serializable { | |||
| } | |||
| public String getContent() { | |||
| return content; | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| @@ -51,7 +52,7 @@ public class WxMpMassOpenIdsMessage implements Serializable { | |||
| } | |||
| public String getMediaId() { | |||
| return mediaId; | |||
| return this.mediaId; | |||
| } | |||
| public void setMediaId(String mediaId) { | |||
| @@ -64,10 +65,9 @@ public class WxMpMassOpenIdsMessage implements Serializable { | |||
| /** | |||
| * OpenId列表,最多支持10,000个 | |||
| * @return | |||
| */ | |||
| public List<String> getToUsers() { | |||
| return toUsers; | |||
| return this.toUsers; | |||
| } | |||
| /** | |||
| @@ -1,18 +1,24 @@ | |||
| package me.chanjar.weixin.mp.bean; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.Serializable; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |||
| import me.chanjar.weixin.mp.util.json.WxLongTimeJsonSerializer; | |||
| import org.apache.commons.io.IOUtils; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.thoughtworks.xstream.annotations.XStreamAlias; | |||
| import com.thoughtworks.xstream.annotations.XStreamConverter; | |||
| import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
| import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||
| import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | |||
| import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | |||
| import org.apache.commons.io.IOUtils; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.Serializable; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * <pre> | |||
| @@ -28,46 +34,49 @@ import java.util.List; | |||
| @XStreamAlias("xml") | |||
| public class WxMpXmlMessage implements Serializable { | |||
| private static final long serialVersionUID = -3586245291677274914L; | |||
| /////////////////////// | |||
| // 以下都是微信推送过来的消息的xml的element所对应的属性 | |||
| /////////////////////// | |||
| @XStreamAlias("ToUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String toUserName; | |||
| @XStreamAlias("FromUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String fromUserName; | |||
| @XStreamAlias("CreateTime") | |||
| @JsonSerialize(using = WxLongTimeJsonSerializer.class) | |||
| private Long createTime; | |||
| @XStreamAlias("MsgType") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String msgType; | |||
| @XStreamAlias("Content") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String content; | |||
| @XStreamAlias("MsgId") | |||
| private Long msgId; | |||
| @XStreamAlias("PicUrl") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String picUrl; | |||
| @XStreamAlias("MediaId") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String mediaId; | |||
| @XStreamAlias("Format") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String format; | |||
| @XStreamAlias("ThumbMediaId") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String thumbMediaId; | |||
| @XStreamAlias("Location_X") | |||
| @@ -80,31 +89,31 @@ public class WxMpXmlMessage implements Serializable { | |||
| private Double scale; | |||
| @XStreamAlias("Label") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String label; | |||
| @XStreamAlias("Title") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String title; | |||
| @XStreamAlias("Description") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String description; | |||
| @XStreamAlias("Url") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String url; | |||
| @XStreamAlias("Event") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String event; | |||
| @XStreamAlias("EventKey") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String eventKey; | |||
| @XStreamAlias("Ticket") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String ticket; | |||
| @XStreamAlias("Latitude") | |||
| @@ -117,7 +126,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| private Double precision; | |||
| @XStreamAlias("Recognition") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String recognition; | |||
| /////////////////////////////////////// | |||
| @@ -127,7 +136,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| * 群发的结果 | |||
| */ | |||
| @XStreamAlias("Status") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String status; | |||
| /** | |||
| * group_id下粉丝数;或者openid_list中的粉丝数 | |||
| @@ -150,26 +159,45 @@ public class WxMpXmlMessage implements Serializable { | |||
| @XStreamAlias("ErrorCount") | |||
| private Integer errorCount; | |||
| /////////////////////////////////////// | |||
| // 客服会话管理相关事件推送 | |||
| /////////////////////////////////////// | |||
| /** | |||
| * 创建或关闭客服会话时的客服帐号 | |||
| */ | |||
| @XStreamAlias("KfAccount") | |||
| private String kfAccount; | |||
| /** | |||
| * 转接客服会话时的转入客服帐号 | |||
| */ | |||
| @XStreamAlias("ToKfAccount") | |||
| private String toKfAccount; | |||
| /** | |||
| * 转接客服会话时的转出客服帐号 | |||
| */ | |||
| @XStreamAlias("FromKfAccount") | |||
| private String fromKfAccount; | |||
| /////////////////////////////////////// | |||
| // 卡券相关事件推送 | |||
| /////////////////////////////////////// | |||
| @XStreamAlias("CardId") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String cardId; | |||
| @XStreamAlias("FriendUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String friendUserName; | |||
| @XStreamAlias("IsGiveByFriend") | |||
| private Integer isGiveByFriend; // 是否为转赠,1代表是,0代表否 | |||
| @XStreamAlias("UserCardCode") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String userCardCode; | |||
| @XStreamAlias("OldUserCardCode") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String oldUserCardCode; | |||
| @XStreamAlias("OuterId") | |||
| @@ -185,7 +213,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| private SendLocationInfo sendLocationInfo = new SendLocationInfo(); | |||
| public String getToUserName() { | |||
| return toUserName; | |||
| return this.toUserName; | |||
| } | |||
| public void setToUserName(String toUserName) { | |||
| @@ -193,7 +221,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Long getCreateTime() { | |||
| return createTime; | |||
| return this.createTime; | |||
| } | |||
| public void setCreateTime(Long createTime) { | |||
| @@ -212,10 +240,9 @@ public class WxMpXmlMessage implements Serializable { | |||
| * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT} | |||
| * </pre> | |||
| * | |||
| * @return | |||
| */ | |||
| public String getMsgType() { | |||
| return msgType; | |||
| return this.msgType; | |||
| } | |||
| /** | |||
| @@ -236,7 +263,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getContent() { | |||
| return content; | |||
| return this.content; | |||
| } | |||
| public void setContent(String content) { | |||
| @@ -244,7 +271,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Long getMsgId() { | |||
| return msgId; | |||
| return this.msgId; | |||
| } | |||
| public void setMsgId(Long msgId) { | |||
| @@ -252,7 +279,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getPicUrl() { | |||
| return picUrl; | |||
| return this.picUrl; | |||
| } | |||
| public void setPicUrl(String picUrl) { | |||
| @@ -260,7 +287,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getMediaId() { | |||
| return mediaId; | |||
| return this.mediaId; | |||
| } | |||
| public void setMediaId(String mediaId) { | |||
| @@ -268,7 +295,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getFormat() { | |||
| return format; | |||
| return this.format; | |||
| } | |||
| public void setFormat(String format) { | |||
| @@ -276,7 +303,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getThumbMediaId() { | |||
| return thumbMediaId; | |||
| return this.thumbMediaId; | |||
| } | |||
| public void setThumbMediaId(String thumbMediaId) { | |||
| @@ -284,7 +311,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getLocationX() { | |||
| return locationX; | |||
| return this.locationX; | |||
| } | |||
| public void setLocationX(Double locationX) { | |||
| @@ -292,7 +319,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getLocationY() { | |||
| return locationY; | |||
| return this.locationY; | |||
| } | |||
| public void setLocationY(Double locationY) { | |||
| @@ -300,7 +327,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getScale() { | |||
| return scale; | |||
| return this.scale; | |||
| } | |||
| public void setScale(Double scale) { | |||
| @@ -308,7 +335,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getLabel() { | |||
| return label; | |||
| return this.label; | |||
| } | |||
| public void setLabel(String label) { | |||
| @@ -316,7 +343,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getTitle() { | |||
| return title; | |||
| return this.title; | |||
| } | |||
| public void setTitle(String title) { | |||
| @@ -324,7 +351,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getDescription() { | |||
| return description; | |||
| return this.description; | |||
| } | |||
| public void setDescription(String description) { | |||
| @@ -332,7 +359,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getUrl() { | |||
| return url; | |||
| return this.url; | |||
| } | |||
| public void setUrl(String url) { | |||
| @@ -340,7 +367,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getEvent() { | |||
| return event; | |||
| return this.event; | |||
| } | |||
| public void setEvent(String event) { | |||
| @@ -348,7 +375,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getEventKey() { | |||
| return eventKey; | |||
| return this.eventKey; | |||
| } | |||
| public void setEventKey(String eventKey) { | |||
| @@ -356,7 +383,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getTicket() { | |||
| return ticket; | |||
| return this.ticket; | |||
| } | |||
| public void setTicket(String ticket) { | |||
| @@ -364,7 +391,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getLatitude() { | |||
| return latitude; | |||
| return this.latitude; | |||
| } | |||
| public void setLatitude(Double latitude) { | |||
| @@ -372,7 +399,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getLongitude() { | |||
| return longitude; | |||
| return this.longitude; | |||
| } | |||
| public void setLongitude(Double longitude) { | |||
| @@ -380,7 +407,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Double getPrecision() { | |||
| return precision; | |||
| return this.precision; | |||
| } | |||
| public void setPrecision(Double precision) { | |||
| @@ -388,7 +415,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getRecognition() { | |||
| return recognition; | |||
| return this.recognition; | |||
| } | |||
| public void setRecognition(String recognition) { | |||
| @@ -396,7 +423,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getFromUserName() { | |||
| return fromUserName; | |||
| return this.fromUserName; | |||
| } | |||
| public void setFromUserName(String fromUserName) { | |||
| @@ -419,30 +446,29 @@ public class WxMpXmlMessage implements Serializable { | |||
| * @param timestamp | |||
| * @param nonce | |||
| * @param msgSignature | |||
| * @return | |||
| */ | |||
| public static WxMpXmlMessage fromEncryptedXml( | |||
| String encryptedXml, | |||
| WxMpConfigStorage wxMpConfigStorage, | |||
| String timestamp, String nonce, String msgSignature) { | |||
| public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, | |||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||
| String msgSignature) { | |||
| WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); | |||
| String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); | |||
| String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, | |||
| encryptedXml); | |||
| return fromXml(plainText); | |||
| } | |||
| public static WxMpXmlMessage fromEncryptedXml( | |||
| InputStream is, | |||
| WxMpConfigStorage wxMpConfigStorage, | |||
| String timestamp, String nonce, String msgSignature) { | |||
| public static WxMpXmlMessage fromEncryptedXml(InputStream is, | |||
| WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, | |||
| String msgSignature) { | |||
| try { | |||
| return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, timestamp, nonce, msgSignature); | |||
| return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, | |||
| timestamp, nonce, msgSignature); | |||
| } catch (IOException e) { | |||
| throw new RuntimeException(e); | |||
| } | |||
| } | |||
| public String getStatus() { | |||
| return status; | |||
| return this.status; | |||
| } | |||
| public void setStatus(String status) { | |||
| @@ -450,7 +476,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getTotalCount() { | |||
| return totalCount; | |||
| return this.totalCount; | |||
| } | |||
| public void setTotalCount(Integer totalCount) { | |||
| @@ -458,7 +484,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getFilterCount() { | |||
| return filterCount; | |||
| return this.filterCount; | |||
| } | |||
| public void setFilterCount(Integer filterCount) { | |||
| @@ -466,7 +492,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getSentCount() { | |||
| return sentCount; | |||
| return this.sentCount; | |||
| } | |||
| public void setSentCount(Integer sentCount) { | |||
| @@ -474,7 +500,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getErrorCount() { | |||
| return errorCount; | |||
| return this.errorCount; | |||
| } | |||
| public void setErrorCount(Integer errorCount) { | |||
| @@ -482,7 +508,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getCardId() { | |||
| return cardId; | |||
| return this.cardId; | |||
| } | |||
| public void setCardId(String cardId) { | |||
| @@ -490,7 +516,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getFriendUserName() { | |||
| return friendUserName; | |||
| return this.friendUserName; | |||
| } | |||
| public void setFriendUserName(String friendUserName) { | |||
| @@ -498,7 +524,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getIsGiveByFriend() { | |||
| return isGiveByFriend; | |||
| return this.isGiveByFriend; | |||
| } | |||
| public void setIsGiveByFriend(Integer isGiveByFriend) { | |||
| @@ -506,7 +532,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getUserCardCode() { | |||
| return userCardCode; | |||
| return this.userCardCode; | |||
| } | |||
| public void setUserCardCode(String userCardCode) { | |||
| @@ -514,7 +540,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getOldUserCardCode() { | |||
| return oldUserCardCode; | |||
| return this.oldUserCardCode; | |||
| } | |||
| public void setOldUserCardCode(String oldUserCardCode) { | |||
| @@ -522,7 +548,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public Integer getOuterId() { | |||
| return outerId; | |||
| return this.outerId; | |||
| } | |||
| public void setOuterId(Integer outerId) { | |||
| @@ -530,7 +556,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public WxMpXmlMessage.ScanCodeInfo getScanCodeInfo() { | |||
| return scanCodeInfo; | |||
| return this.scanCodeInfo; | |||
| } | |||
| public void setScanCodeInfo(WxMpXmlMessage.ScanCodeInfo scanCodeInfo) { | |||
| @@ -538,7 +564,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public WxMpXmlMessage.SendPicsInfo getSendPicsInfo() { | |||
| return sendPicsInfo; | |||
| return this.sendPicsInfo; | |||
| } | |||
| public void setSendPicsInfo(WxMpXmlMessage.SendPicsInfo sendPicsInfo) { | |||
| @@ -546,31 +572,59 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public WxMpXmlMessage.SendLocationInfo getSendLocationInfo() { | |||
| return sendLocationInfo; | |||
| return this.sendLocationInfo; | |||
| } | |||
| public void setSendLocationInfo(WxMpXmlMessage.SendLocationInfo sendLocationInfo) { | |||
| public void setSendLocationInfo( | |||
| WxMpXmlMessage.SendLocationInfo sendLocationInfo) { | |||
| this.sendLocationInfo = sendLocationInfo; | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| public String getToKfAccount() { | |||
| return this.toKfAccount; | |||
| } | |||
| public void setToKfAccount(String toKfAccount) { | |||
| this.toKfAccount = toKfAccount; | |||
| } | |||
| public String getFromKfAccount() { | |||
| return this.fromKfAccount; | |||
| } | |||
| public void setFromKfAccount(String fromKfAccount) { | |||
| this.fromKfAccount = fromKfAccount; | |||
| } | |||
| @XStreamAlias("ScanCodeInfo") | |||
| public static class ScanCodeInfo { | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| @XStreamAlias("ScanType") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String scanType; | |||
| @XStreamAlias("ScanResult") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String scanResult; | |||
| /** | |||
| * 扫描类型,一般是qrcode | |||
| * @return | |||
| */ | |||
| public String getScanType() { | |||
| return scanType; | |||
| return this.scanType; | |||
| } | |||
| public void setScanType(String scanType) { | |||
| @@ -579,10 +633,9 @@ public class WxMpXmlMessage implements Serializable { | |||
| /** | |||
| * 扫描结果,即二维码对应的字符串信息 | |||
| * @return | |||
| */ | |||
| public String getScanResult() { | |||
| return scanResult; | |||
| return this.scanResult; | |||
| } | |||
| public void setScanResult(String scanResult) { | |||
| @@ -593,15 +646,19 @@ public class WxMpXmlMessage implements Serializable { | |||
| @XStreamAlias("SendPicsInfo") | |||
| public static class SendPicsInfo { | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| @XStreamAlias("Count") | |||
| private Long count; | |||
| @XStreamAlias("PicList") | |||
| protected final List<Item> picList = new ArrayList<Item>(); | |||
| protected final List<Item> picList = new ArrayList<>(); | |||
| public Long getCount() { | |||
| return count; | |||
| return this.count; | |||
| } | |||
| public void setCount(Long count) { | |||
| @@ -609,18 +666,23 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public List<Item> getPicList() { | |||
| return picList; | |||
| return this.picList; | |||
| } | |||
| @XStreamAlias("item") | |||
| public static class Item { | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, | |||
| ToStringStyle.JSON_STYLE); | |||
| } | |||
| @XStreamAlias("PicMd5Sum") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String picMd5Sum; | |||
| public String getPicMd5Sum() { | |||
| return picMd5Sum; | |||
| return this.picMd5Sum; | |||
| } | |||
| public void setPicMd5Sum(String picMd5Sum) { | |||
| @@ -633,27 +695,32 @@ public class WxMpXmlMessage implements Serializable { | |||
| public static class SendLocationInfo { | |||
| @XStreamAlias("Location_X") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String locationX; | |||
| @XStreamAlias("Location_Y") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String locationY; | |||
| @XStreamAlias("Scale") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String scale; | |||
| @XStreamAlias("Label") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String label; | |||
| @XStreamAlias("Poiname") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String poiname; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public String getLocationX() { | |||
| return locationX; | |||
| return this.locationX; | |||
| } | |||
| public void setLocationX(String locationX) { | |||
| @@ -661,7 +728,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getLocationY() { | |||
| return locationY; | |||
| return this.locationY; | |||
| } | |||
| public void setLocationY(String locationY) { | |||
| @@ -669,7 +736,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getScale() { | |||
| return scale; | |||
| return this.scale; | |||
| } | |||
| public void setScale(String scale) { | |||
| @@ -677,7 +744,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getLabel() { | |||
| return label; | |||
| return this.label; | |||
| } | |||
| public void setLabel(String label) { | |||
| @@ -685,7 +752,7 @@ public class WxMpXmlMessage implements Serializable { | |||
| } | |||
| public String getPoiname() { | |||
| return poiname; | |||
| return this.poiname; | |||
| } | |||
| public void setPoiname(String poiname) { | |||
| @@ -695,44 +762,6 @@ public class WxMpXmlMessage implements Serializable { | |||
| @Override | |||
| public String toString() { | |||
| return "WxMpXmlMessage{" + | |||
| "toUserName='" + toUserName + '\'' + | |||
| ", fromUserName='" + fromUserName + '\'' + | |||
| ", createTime=" + createTime + | |||
| ", msgType='" + msgType + '\'' + | |||
| ", content='" + content + '\'' + | |||
| ", msgId=" + msgId + | |||
| ", picUrl='" + picUrl + '\'' + | |||
| ", mediaId='" + mediaId + '\'' + | |||
| ", format='" + format + '\'' + | |||
| ", thumbMediaId='" + thumbMediaId + '\'' + | |||
| ", locationX=" + locationX + | |||
| ", locationY=" + locationY + | |||
| ", scale=" + scale + | |||
| ", label='" + label + '\'' + | |||
| ", title='" + title + '\'' + | |||
| ", description='" + description + '\'' + | |||
| ", url='" + url + '\'' + | |||
| ", event='" + event + '\'' + | |||
| ", eventKey='" + eventKey + '\'' + | |||
| ", ticket='" + ticket + '\'' + | |||
| ", latitude=" + latitude + | |||
| ", longitude=" + longitude + | |||
| ", precision=" + precision + | |||
| ", recognition='" + recognition + '\'' + | |||
| ", status='" + status + '\'' + | |||
| ", totalCount=" + totalCount + | |||
| ", filterCount=" + filterCount + | |||
| ", sentCount=" + sentCount + | |||
| ", errorCount=" + errorCount + | |||
| ", cardId='" + cardId + '\'' + | |||
| ", isGiveByFriend=" + isGiveByFriend + | |||
| ", userCardCode='" + userCardCode + '\'' + | |||
| ", oldUserCardCode='" + oldUserCardCode + '\'' + | |||
| ", outerId=" + outerId + | |||
| ", scanCodeInfo=" + scanCodeInfo + | |||
| ", sendPicsInfo=" + sendPicsInfo + | |||
| ", sendLocationInfo=" + sendLocationInfo + | |||
| '}'; | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| } | |||
| @@ -13,6 +13,8 @@ import java.io.Serializable; | |||
| @XStreamAlias("xml") | |||
| public abstract class WxMpXmlOutMessage implements Serializable { | |||
| private static final long serialVersionUID = -381382011286216263L; | |||
| @XStreamAlias("ToUserName") | |||
| @XStreamConverter(value=XStreamCDataConverter.class) | |||
| protected String toUserName; | |||
| @@ -29,7 +31,7 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| protected String msgType; | |||
| public String getToUserName() { | |||
| return toUserName; | |||
| return this.toUserName; | |||
| } | |||
| public void setToUserName(String toUserName) { | |||
| @@ -37,7 +39,7 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| } | |||
| public String getFromUserName() { | |||
| return fromUserName; | |||
| return this.fromUserName; | |||
| } | |||
| public void setFromUserName(String fromUserName) { | |||
| @@ -45,7 +47,7 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| } | |||
| public Long getCreateTime() { | |||
| return createTime; | |||
| return this.createTime; | |||
| } | |||
| public void setCreateTime(Long createTime) { | |||
| @@ -53,7 +55,7 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| } | |||
| public String getMsgType() { | |||
| return msgType; | |||
| return this.msgType; | |||
| } | |||
| public void setMsgType(String msgType) { | |||
| @@ -61,12 +63,11 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| } | |||
| public String toXml() { | |||
| return XStreamTransformer.toXml((Class) this.getClass(), this); | |||
| return XStreamTransformer.toXml((Class<WxMpXmlOutMessage>) this.getClass(), this); | |||
| } | |||
| /** | |||
| * 转换成加密的xml格式 | |||
| * @return | |||
| */ | |||
| public String toEncryptedXml(WxMpConfigStorage wxMpConfigStorage) { | |||
| String plainXml = toXml(); | |||
| @@ -76,7 +77,6 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得文本消息builder | |||
| * @return | |||
| */ | |||
| public static TextBuilder TEXT() { | |||
| return new TextBuilder(); | |||
| @@ -84,7 +84,6 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得图片消息builder | |||
| * @return | |||
| */ | |||
| public static ImageBuilder IMAGE() { | |||
| return new ImageBuilder(); | |||
| @@ -92,7 +91,6 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得语音消息builder | |||
| * @return | |||
| */ | |||
| public static VoiceBuilder VOICE() { | |||
| return new VoiceBuilder(); | |||
| @@ -100,7 +98,6 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得视频消息builder | |||
| * @return | |||
| */ | |||
| public static VideoBuilder VIDEO() { | |||
| return new VideoBuilder(); | |||
| @@ -108,7 +105,6 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得音乐消息builder | |||
| * @return | |||
| */ | |||
| public static MusicBuilder MUSIC() { | |||
| return new MusicBuilder(); | |||
| @@ -116,15 +112,13 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||
| /** | |||
| * 获得图文消息builder | |||
| * @return | |||
| */ | |||
| public static NewsBuilder NEWS() { | |||
| return new NewsBuilder(); | |||
| } | |||
| /** | |||
| /** | |||
| * 获得客服消息builder | |||
| * | |||
| * @return | |||
| */ | |||
| public static TransferCustomerServiceBuilder TRANSFER_CUSTOMER_SERVICE() { | |||
| return new TransferCustomerServiceBuilder(); | |||
| @@ -7,6 +7,8 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||
| @XStreamAlias("xml") | |||
| public class WxMpXmlOutTransferCustomerServiceMessage extends WxMpXmlOutMessage { | |||
| private static final long serialVersionUID = 1850903037285841322L; | |||
| @XStreamAlias("TransInfo") | |||
| protected TransInfo transInfo; | |||
| @@ -15,7 +17,7 @@ public class WxMpXmlOutTransferCustomerServiceMessage extends WxMpXmlOutMessage | |||
| } | |||
| public TransInfo getTransInfo() { | |||
| return transInfo; | |||
| return this.transInfo; | |||
| } | |||
| public void setTransInfo(TransInfo transInfo) { | |||
| @@ -30,7 +32,7 @@ public class WxMpXmlOutTransferCustomerServiceMessage extends WxMpXmlOutMessage | |||
| private String kfAccount; | |||
| public String getKfAccount() { | |||
| return kfAccount; | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| @@ -0,0 +1,105 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.request; | |||
| import java.io.Serializable; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| public class WxMpKfAccountRequest implements Serializable { | |||
| private static final long serialVersionUID = -5451863610674856927L; | |||
| /** | |||
| * kf_account 完整客服账号,格式为:账号前缀@公众号微信号 | |||
| */ | |||
| @SerializedName("kf_account") | |||
| private String kfAccount; | |||
| /** | |||
| * nickname 客服昵称,最长6个汉字或12个英文字符 | |||
| */ | |||
| @SerializedName("nickname") | |||
| private String nickName; | |||
| /** | |||
| * invite_wx 接收绑定邀请的客服微信号 | |||
| */ | |||
| @SerializedName("invite_wx") | |||
| private String inviteWx; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this); | |||
| } | |||
| public String toJson() { | |||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| public String getNickName() { | |||
| return this.nickName; | |||
| } | |||
| public void setNickName(String nickName) { | |||
| this.nickName = nickName; | |||
| } | |||
| public static Builder builder() { | |||
| return new Builder(); | |||
| } | |||
| public String getInviteWx() { | |||
| return this.inviteWx; | |||
| } | |||
| public void setInviteWx(String inviteWx) { | |||
| this.inviteWx = inviteWx; | |||
| } | |||
| public static class Builder { | |||
| private String kfAccount; | |||
| private String nickName; | |||
| private String inviteWx; | |||
| public Builder kfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| return this; | |||
| } | |||
| public Builder nickName(String nickName) { | |||
| this.nickName = nickName; | |||
| return this; | |||
| } | |||
| public Builder inviteWx(String inviteWx) { | |||
| this.inviteWx = inviteWx; | |||
| return this; | |||
| } | |||
| public Builder from(WxMpKfAccountRequest origin) { | |||
| this.kfAccount(origin.kfAccount); | |||
| this.nickName(origin.nickName); | |||
| this.inviteWx(origin.inviteWx); | |||
| return this; | |||
| } | |||
| public WxMpKfAccountRequest build() { | |||
| WxMpKfAccountRequest m = new WxMpKfAccountRequest(); | |||
| m.kfAccount = this.kfAccount; | |||
| m.nickName = this.nickName; | |||
| m.inviteWx = this.inviteWx; | |||
| return m; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.request; | |||
| import java.io.Serializable; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| public class WxMpKfSessionRequest implements Serializable { | |||
| private static final long serialVersionUID = -5451863610674856927L; | |||
| /** | |||
| * kf_account 完整客服账号,格式为:账号前缀@公众号微信号 | |||
| */ | |||
| @SerializedName("kf_account") | |||
| private String kfAccount; | |||
| /** | |||
| * openid 客户openid | |||
| */ | |||
| @SerializedName("openid") | |||
| private String openid; | |||
| public WxMpKfSessionRequest(String kfAccount, String openid) { | |||
| this.kfAccount = kfAccount; | |||
| this.openid = openid; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public String toJson() { | |||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| } | |||
| @@ -0,0 +1,164 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import java.io.Serializable; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.Expose; | |||
| import com.google.gson.annotations.SerializedName; | |||
| /** | |||
| * 客服基本信息以及客服在线状态信息 | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfInfo implements Serializable { | |||
| private static final long serialVersionUID = -5877300750666022290L; | |||
| /** | |||
| * kf_account 完整客服账号,格式为:账号前缀@公众号微信号 | |||
| */ | |||
| @SerializedName("kf_account") | |||
| private String account; | |||
| /** | |||
| * kf_headimgurl 客服头像地址 | |||
| */ | |||
| @SerializedName("kf_headimgurl") | |||
| private String headImgUrl; | |||
| /** | |||
| * kf_id 客服工号 | |||
| */ | |||
| @SerializedName("kf_id") | |||
| private String id; | |||
| /** | |||
| * kf_nick 客服昵称 | |||
| */ | |||
| @SerializedName("kf_nick") | |||
| private String nick; | |||
| /** | |||
| * kf_wx 如果客服帐号已绑定了客服人员微信号,则此处显示微信号 | |||
| */ | |||
| @SerializedName("kf_wx") | |||
| private String wxAccount; | |||
| /** | |||
| * invite_wx 如果客服帐号尚未绑定微信号,但是已经发起了一个绑定邀请,则此处显示绑定邀请的微信号 | |||
| */ | |||
| @SerializedName("invite_wx") | |||
| private String inviteWx; | |||
| /** | |||
| * invite_expire_time 如果客服帐号尚未绑定微信号,但是已经发起过一个绑定邀请,则此处显示为邀请的过期时间,为unix 时间戳 | |||
| */ | |||
| @SerializedName("invite_expire_time") | |||
| private Long inviteExpireTime; | |||
| /** | |||
| * invite_status 邀请的状态,有等待确认“waiting”,被拒绝“rejected”,过期“expired” | |||
| */ | |||
| @SerializedName("invite_status") | |||
| private String inviteStatus; | |||
| /** | |||
| * status 客服在线状态,目前为:1、web 在线 | |||
| */ | |||
| @SerializedName("status") | |||
| private Integer status; | |||
| /** | |||
| * accepted_case 客服当前正在接待的会话数 | |||
| */ | |||
| @Expose | |||
| @SerializedName("accepted_case") | |||
| private Integer acceptedCase; | |||
| public Integer getStatus() { | |||
| return this.status; | |||
| } | |||
| public void setStatus(Integer status) { | |||
| this.status = status; | |||
| } | |||
| public Integer getAcceptedCase() { | |||
| return this.acceptedCase; | |||
| } | |||
| public void setAcceptedCase(Integer acceptedCase) { | |||
| this.acceptedCase = acceptedCase; | |||
| } | |||
| public String getAccount() { | |||
| return this.account; | |||
| } | |||
| public void setAccount(String account) { | |||
| this.account = account; | |||
| } | |||
| public String getHeadImgUrl() { | |||
| return this.headImgUrl; | |||
| } | |||
| public void setHeadImgUrl(String headImgUrl) { | |||
| this.headImgUrl = headImgUrl; | |||
| } | |||
| public String getId() { | |||
| return this.id; | |||
| } | |||
| public void setId(String id) { | |||
| this.id = id; | |||
| } | |||
| public String getNick() { | |||
| return this.nick; | |||
| } | |||
| public void setNick(String nick) { | |||
| this.nick = nick; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public String getWxAccount() { | |||
| return this.wxAccount; | |||
| } | |||
| public void setWxAccount(String wxAccount) { | |||
| this.wxAccount = wxAccount; | |||
| } | |||
| public String getInviteWx() { | |||
| return this.inviteWx; | |||
| } | |||
| public void setInviteWx(String inviteWx) { | |||
| this.inviteWx = inviteWx; | |||
| } | |||
| public Long getInviteExpireTime() { | |||
| return this.inviteExpireTime; | |||
| } | |||
| public void setInviteExpireTime(Long inviteExpireTime) { | |||
| this.inviteExpireTime = inviteExpireTime; | |||
| } | |||
| public String getInviteStatus() { | |||
| return this.inviteStatus; | |||
| } | |||
| public void setInviteStatus(String inviteStatus) { | |||
| this.inviteStatus = inviteStatus; | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import java.util.List; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfList { | |||
| @SerializedName("kf_list") | |||
| private List<WxMpKfInfo> kfList; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public List<WxMpKfInfo> getKfList() { | |||
| return this.kfList; | |||
| } | |||
| public void setKfList(List<WxMpKfInfo> kfList) { | |||
| this.kfList = kfList; | |||
| } | |||
| public static WxMpKfList fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfList.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import java.util.List; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/15. | |||
| */ | |||
| public class WxMpKfMsgList { | |||
| @SerializedName("recordlist") | |||
| private List<WxMpKfMsgRecord> records; | |||
| @SerializedName("number") | |||
| private Integer number; | |||
| @SerializedName("msgid") | |||
| private Long msgId; | |||
| public List<WxMpKfMsgRecord> getRecords() { | |||
| return records; | |||
| } | |||
| public void setRecords(List<WxMpKfMsgRecord> records) { | |||
| this.records = records; | |||
| } | |||
| public Integer getNumber() { | |||
| return number; | |||
| } | |||
| public void setNumber(Integer number) { | |||
| this.number = number; | |||
| } | |||
| public Long getMsgId() { | |||
| return msgId; | |||
| } | |||
| public void setMsgId(Long msgId) { | |||
| this.msgId = msgId; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public static WxMpKfMsgList fromJson(String responseContent) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(responseContent, WxMpKfMsgList.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,87 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxLongTimeJsonSerializer; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/18. | |||
| */ | |||
| public class WxMpKfMsgRecord { | |||
| /** | |||
| * worker 完整客服帐号,格式为:帐号前缀@公众号微信号 | |||
| */ | |||
| @SerializedName("worker") | |||
| private String worker; | |||
| /** | |||
| * openid 用户标识 | |||
| */ | |||
| @SerializedName("openid") | |||
| private String openid; | |||
| /** | |||
| * opercode 操作码,2002(客服发送信息),2003(客服接收消息) | |||
| */ | |||
| @SerializedName("opercode") | |||
| private Integer operateCode; | |||
| /** | |||
| * text 聊天记录 | |||
| */ | |||
| @SerializedName("text") | |||
| private String text; | |||
| /** | |||
| * time 操作时间,unix时间戳 | |||
| */ | |||
| @SerializedName("time") | |||
| @JsonSerialize(using = WxLongTimeJsonSerializer.class) | |||
| private Long time; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public String getWorker() { | |||
| return worker; | |||
| } | |||
| public void setWorker(String worker) { | |||
| this.worker = worker; | |||
| } | |||
| public String getOpenid() { | |||
| return openid; | |||
| } | |||
| public void setOpenid(String openid) { | |||
| this.openid = openid; | |||
| } | |||
| public String getText() { | |||
| return text; | |||
| } | |||
| public void setText(String text) { | |||
| this.text = text; | |||
| } | |||
| public Long getTime() { | |||
| return time; | |||
| } | |||
| public void setTime(Long time) { | |||
| this.time = time; | |||
| } | |||
| public Integer getOperateCode() { | |||
| return operateCode; | |||
| } | |||
| public void setOperateCode(Integer operateCode) { | |||
| this.operateCode = operateCode; | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import java.util.List; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfOnlineList { | |||
| @SerializedName("kf_online_list") | |||
| private List<WxMpKfInfo> kfOnlineList; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public List<WxMpKfInfo> getKfOnlineList() { | |||
| return this.kfOnlineList; | |||
| } | |||
| public void setKfOnlineList(List<WxMpKfInfo> kfOnlineList) { | |||
| this.kfOnlineList = kfOnlineList; | |||
| } | |||
| public static WxMpKfOnlineList fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfOnlineList.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfSession { | |||
| /** | |||
| * kf_account 正在接待的客服,为空表示没有人在接待 | |||
| */ | |||
| @SerializedName("kf_account") | |||
| private String kfAccount; | |||
| /** | |||
| * createtime 会话接入的时间,UNIX时间戳 | |||
| * 该返回值 存在于 获取客服会话列表接口 | |||
| */ | |||
| @SerializedName("createtime") | |||
| private long createTime; | |||
| /** | |||
| * latest_time 粉丝的最后一条消息的时间,UNIX时间戳 | |||
| * 该返回值 存在于 获取未接入会话列表接口 | |||
| */ | |||
| @SerializedName("latest_time") | |||
| private long latestTime; | |||
| /** | |||
| * openid 客户openid | |||
| */ | |||
| @SerializedName("openid") | |||
| private String openid; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| public long getCreateTime() { | |||
| return this.createTime; | |||
| } | |||
| public void setCreateTime(long createTime) { | |||
| this.createTime = createTime; | |||
| } | |||
| public String getOpenid() { | |||
| return this.openid; | |||
| } | |||
| public void setOpenid(String openid) { | |||
| this.openid = openid; | |||
| } | |||
| public long getLatestTime() { | |||
| return this.latestTime; | |||
| } | |||
| public void setLatestTime(long latestTime) { | |||
| this.latestTime = latestTime; | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfSessionGetResult { | |||
| /** | |||
| * kf_account 正在接待的客服,为空表示没有人在接待 | |||
| */ | |||
| @SerializedName("kf_account") | |||
| private String kfAccount; | |||
| /** | |||
| * createtime 会话接入的时间 ,UNIX时间戳 | |||
| */ | |||
| @SerializedName("createtime") | |||
| private long createTime; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public static WxMpKfSessionGetResult fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpKfSessionGetResult.class); | |||
| } | |||
| public String getKfAccount() { | |||
| return this.kfAccount; | |||
| } | |||
| public void setKfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| } | |||
| public long getCreateTime() { | |||
| return this.createTime; | |||
| } | |||
| public void setCreateTime(long createTime) { | |||
| this.createTime = createTime; | |||
| } | |||
| } | |||
| @@ -0,0 +1,41 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import java.util.List; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfSessionList { | |||
| /** | |||
| * 会话列表 | |||
| */ | |||
| @SerializedName("sessionlist") | |||
| private List<WxMpKfSession> kfSessionList; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public static WxMpKfSessionList fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, | |||
| WxMpKfSessionList.class); | |||
| } | |||
| public List<WxMpKfSession> getKfSessionList() { | |||
| return this.kfSessionList; | |||
| } | |||
| public void setKfSessionList(List<WxMpKfSession> kfSessionList) { | |||
| this.kfSessionList = kfSessionList; | |||
| } | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package me.chanjar.weixin.mp.bean.kefu.result; | |||
| import java.util.List; | |||
| import org.apache.commons.lang3.builder.ToStringBuilder; | |||
| import org.apache.commons.lang3.builder.ToStringStyle; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||
| /** | |||
| * | |||
| * @author Binary Wang | |||
| * | |||
| */ | |||
| public class WxMpKfSessionWaitCaseList { | |||
| /** | |||
| * count 未接入会话数量 | |||
| */ | |||
| @SerializedName("count") | |||
| private Long count; | |||
| /** | |||
| * waitcaselist 未接入会话列表,最多返回100条数据 | |||
| */ | |||
| @SerializedName("waitcaselist") | |||
| private List<WxMpKfSession> kfSessionWaitCaseList; | |||
| @Override | |||
| public String toString() { | |||
| return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE); | |||
| } | |||
| public static WxMpKfSessionWaitCaseList fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, | |||
| WxMpKfSessionWaitCaseList.class); | |||
| } | |||
| public List<WxMpKfSession> getKfSessionWaitCaseList() { | |||
| return this.kfSessionWaitCaseList; | |||
| } | |||
| public void setKfSessionWaitCaseList(List<WxMpKfSession> kfSessionWaitCaseList) { | |||
| this.kfSessionWaitCaseList = kfSessionWaitCaseList; | |||
| } | |||
| } | |||
| @@ -14,18 +14,18 @@ import me.chanjar.weixin.mp.bean.WxMpXmlOutTransferCustomerServiceMessage; | |||
| public final class TransferCustomerServiceBuilder extends BaseBuilder<TransferCustomerServiceBuilder, WxMpXmlOutTransferCustomerServiceMessage> { | |||
| private String kfAccount; | |||
| public TransferCustomerServiceBuilder kfAccount(String kfAccount) { | |||
| this.kfAccount = kfAccount; | |||
| public TransferCustomerServiceBuilder kfAccount(String kf) { | |||
| this.kfAccount = kf; | |||
| return this; | |||
| } | |||
| @Override | |||
| public WxMpXmlOutTransferCustomerServiceMessage build() { | |||
| WxMpXmlOutTransferCustomerServiceMessage m = new WxMpXmlOutTransferCustomerServiceMessage(); | |||
| setCommon(m); | |||
| if(StringUtils.isNotBlank(kfAccount)){ | |||
| if(StringUtils.isNotBlank(this.kfAccount)){ | |||
| WxMpXmlOutTransferCustomerServiceMessage.TransInfo transInfo = new WxMpXmlOutTransferCustomerServiceMessage.TransInfo(); | |||
| transInfo.setKfAccount(kfAccount); | |||
| transInfo.setKfAccount(this.kfAccount); | |||
| m.setTransInfo(transInfo); | |||
| } | |||
| return m; | |||
| @@ -45,4 +45,9 @@ public class WxMpQrCodeTicket implements Serializable { | |||
| public static WxMpQrCodeTicket fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpQrCodeTicket.class); | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| } | |||
| @@ -44,4 +44,9 @@ public class WxMpUserList { | |||
| public static WxMpUserList fromJson(String json) { | |||
| return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class); | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return WxMpGsonBuilder.INSTANCE.create().toJson(this); | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package me.chanjar.weixin.mp.util.json; | |||
| import java.io.IOException; | |||
| import java.text.SimpleDateFormat; | |||
| import com.fasterxml.jackson.core.JsonGenerator; | |||
| import com.fasterxml.jackson.core.JsonProcessingException; | |||
| import com.fasterxml.jackson.databind.JsonSerializer; | |||
| import com.fasterxml.jackson.databind.SerializerProvider; | |||
| /** | |||
| * Created by Binary Wang on 2016/7/13. | |||
| */ | |||
| public class WxLongTimeJsonSerializer extends JsonSerializer<Long> { | |||
| private static SimpleDateFormat DF = new SimpleDateFormat( | |||
| "yyyy-MM-dd HH:mm:ss"); | |||
| @Override | |||
| public void serialize(Long value, JsonGenerator gen, | |||
| SerializerProvider serializers) | |||
| throws IOException, JsonProcessingException { | |||
| gen.writeString(DF.format(value * 1000)); | |||
| } | |||
| } | |||