@@ -1,300 +1,300 @@ | |||||
/** | |||||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||||
* | |||||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||||
* <p> | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | |||||
// ------------------------------------------------------------------------ | |||||
/** | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | |||||
package me.chanjar.weixin.common.util.crypto; | |||||
import java.io.StringReader; | |||||
import java.nio.charset.Charset; | |||||
import java.util.Arrays; | |||||
import java.util.Random; | |||||
import javax.crypto.Cipher; | |||||
import javax.crypto.spec.IvParameterSpec; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import javax.xml.parsers.ParserConfigurationException; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Element; | |||||
import org.xml.sax.InputSource; | |||||
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); | |||||
} | |||||
} | |||||
}; | |||||
protected byte[] aesKey; | |||||
protected String token; | |||||
protected String appidOrCorpid; | |||||
public WxCryptUtil() { | |||||
super(); | |||||
} | |||||
/** | |||||
* 构造函数 | |||||
* | |||||
* @param token 公众平台上,开发者设置的token | |||||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||||
* @param appidOrCorpid 公众平台appid/corpid | |||||
*/ | |||||
public WxCryptUtil(String token, String encodingAesKey, | |||||
String appidOrCorpid) { | |||||
this.token = token; | |||||
this.appidOrCorpid = appidOrCorpid; | |||||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||||
} | |||||
static String extractEncryptPart(String xml) { | |||||
try { | |||||
DocumentBuilder db = builderLocal.get(); | |||||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||||
Element root = document.getDocumentElement(); | |||||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | |||||
* 将公众平台回复用户的消息加密打包. | |||||
* <ol> | |||||
* <li>对要发送的消息进行AES-CBC加密</li> | |||||
* <li>生成安全签名</li> | |||||
* <li>将消息密文和安全签名打包成xml格式</li> | |||||
* </ol> | |||||
* | |||||
* @param plainText 公众平台待回复用户的消息,xml格式的字符串 | |||||
* @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 | |||||
*/ | |||||
public String encrypt(String plainText) { | |||||
// 加密 | |||||
String encryptedXml = encrypt(genRandomStr(), plainText); | |||||
// 生成安全签名 | |||||
String timeStamp = Long.toString(System.currentTimeMillis() / 1000l); | |||||
String nonce = genRandomStr(); | |||||
String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedXml); | |||||
String result = generateXml(encryptedXml, signature, timeStamp, nonce); | |||||
return result; | |||||
} | |||||
/** | |||||
* 对明文进行加密. | |||||
* | |||||
* @param plainText 需要加密的明文 | |||||
* @return 加密后base64编码的字符串 | |||||
*/ | |||||
protected String encrypt(String randomStr, String plainText) { | |||||
ByteGroup byteCollector = new ByteGroup(); | |||||
byte[] randomStringBytes = randomStr.getBytes(CHARSET); | |||||
byte[] plainTextBytes = plainText.getBytes(CHARSET); | |||||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | |||||
plainTextBytes.length); | |||||
byte[] appIdBytes = this.appidOrCorpid.getBytes(CHARSET); | |||||
// randomStr + networkBytesOrder + text + appid | |||||
byteCollector.addBytes(randomStringBytes); | |||||
byteCollector.addBytes(bytesOfSizeInNetworkOrder); | |||||
byteCollector.addBytes(plainTextBytes); | |||||
byteCollector.addBytes(appIdBytes); | |||||
// ... + pad: 使用自定义的填充方式对明文进行补位填充 | |||||
byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); | |||||
byteCollector.addBytes(padBytes); | |||||
// 获得最终的字节流, 未加密 | |||||
byte[] unencrypted = byteCollector.toBytes(); | |||||
try { | |||||
// 设置加密模式为AES的CBC模式 | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||||
SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES"); | |||||
IvParameterSpec iv = new IvParameterSpec(this.aesKey, 0, 16); | |||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); | |||||
// 加密 | |||||
byte[] encrypted = cipher.doFinal(unencrypted); | |||||
// 使用BASE64对加密后的字符串进行编码 | |||||
String base64Encrypted = base64.encodeToString(encrypted); | |||||
return base64Encrypted; | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | |||||
* 检验消息的真实性,并且获取解密后的明文. | |||||
* <ol> | |||||
* <li>利用收到的密文生成安全签名,进行签名验证</li> | |||||
* <li>若验证通过,则提取xml中的加密消息</li> | |||||
* <li>对消息进行解密</li> | |||||
* </ol> | |||||
* | |||||
* @param msgSignature 签名串,对应URL参数的msg_signature | |||||
* @param timeStamp 时间戳,对应URL参数的timestamp | |||||
* @param nonce 随机串,对应URL参数的nonce | |||||
* @param encryptedXml 密文,对应POST请求的数据 | |||||
* @return 解密后的原文 | |||||
*/ | |||||
public String decrypt(String msgSignature, String timeStamp, String nonce, | |||||
String encryptedXml) { | |||||
// 密钥,公众账号的app corpSecret | |||||
// 提取密文 | |||||
String cipherText = extractEncryptPart(encryptedXml); | |||||
// 验证安全签名 | |||||
String signature = SHA1.gen(this.token, timeStamp, nonce, cipherText); | |||||
if (!signature.equals(msgSignature)) { | |||||
throw new RuntimeException("加密消息签名校验失败"); | |||||
} | |||||
// 解密 | |||||
String result = decrypt(cipherText); | |||||
return result; | |||||
} | |||||
/** | |||||
* 对密文进行解密. | |||||
* | |||||
* @param cipherText 需要解密的密文 | |||||
* @return 解密得到的明文 | |||||
*/ | |||||
public String decrypt(String cipherText) { | |||||
byte[] original; | |||||
try { | |||||
// 设置解密模式为AES的CBC模式 | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||||
SecretKeySpec key_spec = new SecretKeySpec(this.aesKey, "AES"); | |||||
IvParameterSpec iv = new IvParameterSpec( | |||||
Arrays.copyOfRange(this.aesKey, 0, 16)); | |||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||||
// 使用BASE64对密文进行解码 | |||||
byte[] encrypted = Base64.decodeBase64(cipherText); | |||||
// 解密 | |||||
original = cipher.doFinal(encrypted); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
String xmlContent, from_appid; | |||||
try { | |||||
// 去除补位字符 | |||||
byte[] bytes = PKCS7Encoder.decode(original); | |||||
// 分离16位随机字符串,网络字节序和AppId | |||||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); | |||||
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), CHARSET); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
// appid不相同的情况 | |||||
if (!from_appid.equals(this.appidOrCorpid)) { | |||||
throw new RuntimeException("AppID不正确"); | |||||
} | |||||
return xmlContent; | |||||
} | |||||
/** | |||||
* 将一个数字转换成生成4个字节的网络字节序bytes数组 | |||||
* | |||||
* @param number | |||||
*/ | |||||
private static byte[] number2BytesInNetworkOrder(int number) { | |||||
byte[] orderBytes = new byte[4]; | |||||
orderBytes[3] = (byte) (number & 0xFF); | |||||
orderBytes[2] = (byte) (number >> 8 & 0xFF); | |||||
orderBytes[1] = (byte) (number >> 16 & 0xFF); | |||||
orderBytes[0] = (byte) (number >> 24 & 0xFF); | |||||
return orderBytes; | |||||
} | |||||
/** | |||||
* 4个字节的网络字节序bytes数组还原成一个数字 | |||||
* | |||||
* @param bytesInNetworkOrder | |||||
*/ | |||||
private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { | |||||
int sourceNumber = 0; | |||||
for (int i = 0; i < 4; i++) { | |||||
sourceNumber <<= 8; | |||||
sourceNumber |= bytesInNetworkOrder[i] & 0xff; | |||||
} | |||||
return sourceNumber; | |||||
} | |||||
/** | |||||
* 随机生成16位字符串 | |||||
*/ | |||||
private static String genRandomStr() { | |||||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||||
Random random = new Random(); | |||||
StringBuffer sb = new StringBuffer(); | |||||
for (int i = 0; i < 16; i++) { | |||||
int number = random.nextInt(base.length()); | |||||
sb.append(base.charAt(number)); | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* 生成xml消息 | |||||
* | |||||
* @param encrypt 加密后的消息密文 | |||||
* @param signature 安全签名 | |||||
* @param timestamp 时间戳 | |||||
* @param nonce 随机字符串 | |||||
* @return 生成的xml字符串 | |||||
*/ | |||||
private static 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); | |||||
} | |||||
} | |||||
/** | |||||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||||
* | |||||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||||
* <p> | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | |||||
// ------------------------------------------------------------------------ | |||||
/** | |||||
* 针对org.apache.commons.codec.binary.Base64, | |||||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||||
*/ | |||||
package me.chanjar.weixin.common.util.crypto; | |||||
import java.io.StringReader; | |||||
import java.nio.charset.Charset; | |||||
import java.util.Arrays; | |||||
import java.util.Random; | |||||
import javax.crypto.Cipher; | |||||
import javax.crypto.spec.IvParameterSpec; | |||||
import javax.crypto.spec.SecretKeySpec; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import javax.xml.parsers.ParserConfigurationException; | |||||
import org.apache.commons.codec.binary.Base64; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Element; | |||||
import org.xml.sax.InputSource; | |||||
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); | |||||
} | |||||
} | |||||
}; | |||||
protected byte[] aesKey; | |||||
protected String token; | |||||
protected String appidOrCorpid; | |||||
public WxCryptUtil() { | |||||
super(); | |||||
} | |||||
/** | |||||
* 构造函数 | |||||
* | |||||
* @param token 公众平台上,开发者设置的token | |||||
* @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey | |||||
* @param appidOrCorpid 公众平台appid/corpid | |||||
*/ | |||||
public WxCryptUtil(String token, String encodingAesKey, | |||||
String appidOrCorpid) { | |||||
this.token = token; | |||||
this.appidOrCorpid = appidOrCorpid; | |||||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||||
} | |||||
static String extractEncryptPart(String xml) { | |||||
try { | |||||
DocumentBuilder db = builderLocal.get(); | |||||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||||
Element root = document.getDocumentElement(); | |||||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | |||||
* 将公众平台回复用户的消息加密打包. | |||||
* <ol> | |||||
* <li>对要发送的消息进行AES-CBC加密</li> | |||||
* <li>生成安全签名</li> | |||||
* <li>将消息密文和安全签名打包成xml格式</li> | |||||
* </ol> | |||||
* | |||||
* @param plainText 公众平台待回复用户的消息,xml格式的字符串 | |||||
* @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串 | |||||
*/ | |||||
public String encrypt(String plainText) { | |||||
// 加密 | |||||
String encryptedXml = encrypt(genRandomStr(), plainText); | |||||
// 生成安全签名 | |||||
String timeStamp = Long.toString(System.currentTimeMillis() / 1000l); | |||||
String nonce = genRandomStr(); | |||||
String signature = SHA1.gen(this.token, timeStamp, nonce, encryptedXml); | |||||
String result = generateXml(encryptedXml, signature, timeStamp, nonce); | |||||
return result; | |||||
} | |||||
/** | |||||
* 对明文进行加密. | |||||
* | |||||
* @param plainText 需要加密的明文 | |||||
* @return 加密后base64编码的字符串 | |||||
*/ | |||||
protected String encrypt(String randomStr, String plainText) { | |||||
ByteGroup byteCollector = new ByteGroup(); | |||||
byte[] randomStringBytes = randomStr.getBytes(CHARSET); | |||||
byte[] plainTextBytes = plainText.getBytes(CHARSET); | |||||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | |||||
plainTextBytes.length); | |||||
byte[] appIdBytes = this.appidOrCorpid.getBytes(CHARSET); | |||||
// randomStr + networkBytesOrder + text + appid | |||||
byteCollector.addBytes(randomStringBytes); | |||||
byteCollector.addBytes(bytesOfSizeInNetworkOrder); | |||||
byteCollector.addBytes(plainTextBytes); | |||||
byteCollector.addBytes(appIdBytes); | |||||
// ... + pad: 使用自定义的填充方式对明文进行补位填充 | |||||
byte[] padBytes = PKCS7Encoder.encode(byteCollector.size()); | |||||
byteCollector.addBytes(padBytes); | |||||
// 获得最终的字节流, 未加密 | |||||
byte[] unencrypted = byteCollector.toBytes(); | |||||
try { | |||||
// 设置加密模式为AES的CBC模式 | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||||
SecretKeySpec keySpec = new SecretKeySpec(this.aesKey, "AES"); | |||||
IvParameterSpec iv = new IvParameterSpec(this.aesKey, 0, 16); | |||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv); | |||||
// 加密 | |||||
byte[] encrypted = cipher.doFinal(unencrypted); | |||||
// 使用BASE64对加密后的字符串进行编码 | |||||
String base64Encrypted = base64.encodeToString(encrypted); | |||||
return base64Encrypted; | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
/** | |||||
* 检验消息的真实性,并且获取解密后的明文. | |||||
* <ol> | |||||
* <li>利用收到的密文生成安全签名,进行签名验证</li> | |||||
* <li>若验证通过,则提取xml中的加密消息</li> | |||||
* <li>对消息进行解密</li> | |||||
* </ol> | |||||
* | |||||
* @param msgSignature 签名串,对应URL参数的msg_signature | |||||
* @param timeStamp 时间戳,对应URL参数的timestamp | |||||
* @param nonce 随机串,对应URL参数的nonce | |||||
* @param encryptedXml 密文,对应POST请求的数据 | |||||
* @return 解密后的原文 | |||||
*/ | |||||
public String decrypt(String msgSignature, String timeStamp, String nonce, | |||||
String encryptedXml) { | |||||
// 密钥,公众账号的app corpSecret | |||||
// 提取密文 | |||||
String cipherText = extractEncryptPart(encryptedXml); | |||||
// 验证安全签名 | |||||
String signature = SHA1.gen(this.token, timeStamp, nonce, cipherText); | |||||
if (!signature.equals(msgSignature)) { | |||||
throw new RuntimeException("加密消息签名校验失败"); | |||||
} | |||||
// 解密 | |||||
String result = decrypt(cipherText); | |||||
return result; | |||||
} | |||||
/** | |||||
* 对密文进行解密. | |||||
* | |||||
* @param cipherText 需要解密的密文 | |||||
* @return 解密得到的明文 | |||||
*/ | |||||
public String decrypt(String cipherText) { | |||||
byte[] original; | |||||
try { | |||||
// 设置解密模式为AES的CBC模式 | |||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||||
SecretKeySpec key_spec = new SecretKeySpec(this.aesKey, "AES"); | |||||
IvParameterSpec iv = new IvParameterSpec( | |||||
Arrays.copyOfRange(this.aesKey, 0, 16)); | |||||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||||
// 使用BASE64对密文进行解码 | |||||
byte[] encrypted = Base64.decodeBase64(cipherText); | |||||
// 解密 | |||||
original = cipher.doFinal(encrypted); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
String xmlContent, from_appid; | |||||
try { | |||||
// 去除补位字符 | |||||
byte[] bytes = PKCS7Encoder.decode(original); | |||||
// 分离16位随机字符串,网络字节序和AppId | |||||
byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20); | |||||
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), CHARSET); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
// appid不相同的情况 | |||||
if (!from_appid.equals(this.appidOrCorpid)) { | |||||
throw new RuntimeException("AppID不正确"); | |||||
} | |||||
return xmlContent; | |||||
} | |||||
/** | |||||
* 将一个数字转换成生成4个字节的网络字节序bytes数组 | |||||
* | |||||
* @param number | |||||
*/ | |||||
private static byte[] number2BytesInNetworkOrder(int number) { | |||||
byte[] orderBytes = new byte[4]; | |||||
orderBytes[3] = (byte) (number & 0xFF); | |||||
orderBytes[2] = (byte) (number >> 8 & 0xFF); | |||||
orderBytes[1] = (byte) (number >> 16 & 0xFF); | |||||
orderBytes[0] = (byte) (number >> 24 & 0xFF); | |||||
return orderBytes; | |||||
} | |||||
/** | |||||
* 4个字节的网络字节序bytes数组还原成一个数字 | |||||
* | |||||
* @param bytesInNetworkOrder | |||||
*/ | |||||
private static int bytesNetworkOrder2Number(byte[] bytesInNetworkOrder) { | |||||
int sourceNumber = 0; | |||||
for (int i = 0; i < 4; i++) { | |||||
sourceNumber <<= 8; | |||||
sourceNumber |= bytesInNetworkOrder[i] & 0xff; | |||||
} | |||||
return sourceNumber; | |||||
} | |||||
/** | |||||
* 随机生成16位字符串 | |||||
*/ | |||||
private static String genRandomStr() { | |||||
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||||
Random random = new Random(); | |||||
StringBuffer sb = new StringBuffer(); | |||||
for (int i = 0; i < 16; i++) { | |||||
int number = random.nextInt(base.length()); | |||||
sb.append(base.charAt(number)); | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* 生成xml消息 | |||||
* | |||||
* @param encrypt 加密后的消息密文 | |||||
* @param signature 安全签名 | |||||
* @param timestamp 时间戳 | |||||
* @param nonce 随机字符串 | |||||
* @return 生成的xml字符串 | |||||
*/ | |||||
private static 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); | |||||
} | |||||
} |
@@ -1,103 +1,103 @@ | |||||
package me.chanjar.weixin.common.util.crypto; | |||||
import org.testng.annotations.Test; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Element; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.InputSource; | |||||
import org.xml.sax.SAXException; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import javax.xml.parsers.ParserConfigurationException; | |||||
import java.io.IOException; | |||||
import java.io.StringReader; | |||||
import static org.testng.Assert.assertEquals; | |||||
import static org.testng.Assert.fail; | |||||
@Test | |||||
public class WxCryptUtilTest { | |||||
String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; | |||||
String token = "pamtest"; | |||||
String timestamp = "1409304348"; | |||||
String nonce = "xxxxxx"; | |||||
String appId = "wxb11529c136998cb6"; | |||||
String randomStr = "aaaabbbbccccdddd"; | |||||
String xmlFormat = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>"; | |||||
String replyMsg = "我是中文abcd123"; | |||||
String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg=="; | |||||
String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>"; | |||||
String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; | |||||
public void testNormal() throws ParserConfigurationException, SAXException, IOException { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
String encryptedXml = pc.encrypt(this.replyMsg); | |||||
System.out.println(encryptedXml); | |||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||||
Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); | |||||
Element root = document.getDocumentElement(); | |||||
String cipherText = root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
System.out.println(cipherText); | |||||
String msgSignature = root.getElementsByTagName("MsgSignature").item(0).getTextContent(); | |||||
System.out.println(msgSignature); | |||||
String timestamp = root.getElementsByTagName("TimeStamp").item(0).getTextContent(); | |||||
System.out.println(timestamp); | |||||
String nonce = root.getElementsByTagName("Nonce").item(0).getTextContent(); | |||||
System.out.println(nonce); | |||||
String messageText = String.format(this.xmlFormat, cipherText); | |||||
System.out.println(messageText); | |||||
// 第三方收到企业号平台发送的消息 | |||||
String plainMessage = pc.decrypt(cipherText); | |||||
System.out.println(plainMessage); | |||||
assertEquals(plainMessage, this.replyMsg); | |||||
} | |||||
public void testAesEncrypt() { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
assertEquals(pc.encrypt(this.randomStr, this.replyMsg), this.afterAesEncrypt); | |||||
} | |||||
public void testAesEncrypt2() { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
assertEquals(pc.encrypt(this.randomStr, this.replyMsg2), this.afterAesEncrypt2); | |||||
} | |||||
public void testValidateSignatureError() throws ParserConfigurationException, SAXException, | |||||
IOException { | |||||
try { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
String afterEncrpt = pc.encrypt(this.replyMsg); | |||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||||
DocumentBuilder db = dbf.newDocumentBuilder(); | |||||
StringReader sr = new StringReader(afterEncrpt); | |||||
InputSource is = new InputSource(sr); | |||||
Document document = db.parse(is); | |||||
Element root = document.getDocumentElement(); | |||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt"); | |||||
String encrypt = nodelist1.item(0).getTextContent(); | |||||
String fromXML = String.format(this.xmlFormat, encrypt); | |||||
pc.decrypt("12345", this.timestamp, this.nonce, fromXML); // 这里签名错误 | |||||
} catch (RuntimeException e) { | |||||
assertEquals(e.getMessage(), "加密消息签名校验失败"); | |||||
return; | |||||
} | |||||
fail("错误流程不抛出异常???"); | |||||
} | |||||
} | |||||
package me.chanjar.weixin.common.util.crypto; | |||||
import org.testng.annotations.Test; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.Element; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.InputSource; | |||||
import org.xml.sax.SAXException; | |||||
import javax.xml.parsers.DocumentBuilder; | |||||
import javax.xml.parsers.DocumentBuilderFactory; | |||||
import javax.xml.parsers.ParserConfigurationException; | |||||
import java.io.IOException; | |||||
import java.io.StringReader; | |||||
import static org.testng.Assert.assertEquals; | |||||
import static org.testng.Assert.fail; | |||||
@Test | |||||
public class WxCryptUtilTest { | |||||
String encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG"; | |||||
String token = "pamtest"; | |||||
String timestamp = "1409304348"; | |||||
String nonce = "xxxxxx"; | |||||
String appId = "wxb11529c136998cb6"; | |||||
String randomStr = "aaaabbbbccccdddd"; | |||||
String xmlFormat = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>"; | |||||
String replyMsg = "我是中文abcd123"; | |||||
String afterAesEncrypt = "jn1L23DB+6ELqJ+6bruv21Y6MD7KeIfP82D6gU39rmkgczbWwt5+3bnyg5K55bgVtVzd832WzZGMhkP72vVOfg=="; | |||||
String replyMsg2 = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>"; | |||||
String afterAesEncrypt2 = "jn1L23DB+6ELqJ+6bruv23M2GmYfkv0xBh2h+XTBOKVKcgDFHle6gqcZ1cZrk3e1qjPQ1F4RsLWzQRG9udbKWesxlkupqcEcW7ZQweImX9+wLMa0GaUzpkycA8+IamDBxn5loLgZpnS7fVAbExOkK5DYHBmv5tptA9tklE/fTIILHR8HLXa5nQvFb3tYPKAlHF3rtTeayNf0QuM+UW/wM9enGIDIJHF7CLHiDNAYxr+r+OrJCmPQyTy8cVWlu9iSvOHPT/77bZqJucQHQ04sq7KZI27OcqpQNSto2OdHCoTccjggX5Z9Mma0nMJBU+jLKJ38YB1fBIz+vBzsYjrTmFQ44YfeEuZ+xRTQwr92vhA9OxchWVINGC50qE/6lmkwWTwGX9wtQpsJKhP+oS7rvTY8+VdzETdfakjkwQ5/Xka042OlUb1/slTwo4RscuQ+RdxSGvDahxAJ6+EAjLt9d8igHngxIbf6YyqqROxuxqIeIch3CssH/LqRs+iAcILvApYZckqmA7FNERspKA5f8GoJ9sv8xmGvZ9Yrf57cExWtnX8aCMMaBropU/1k+hKP5LVdzbWCG0hGwx/dQudYR/eXp3P0XxjlFiy+9DMlaFExWUZQDajPkdPrEeOwofJb"; | |||||
public void testNormal() throws ParserConfigurationException, SAXException, IOException { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
String encryptedXml = pc.encrypt(this.replyMsg); | |||||
System.out.println(encryptedXml); | |||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||||
Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); | |||||
Element root = document.getDocumentElement(); | |||||
String cipherText = root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||||
System.out.println(cipherText); | |||||
String msgSignature = root.getElementsByTagName("MsgSignature").item(0).getTextContent(); | |||||
System.out.println(msgSignature); | |||||
String timestamp = root.getElementsByTagName("TimeStamp").item(0).getTextContent(); | |||||
System.out.println(timestamp); | |||||
String nonce = root.getElementsByTagName("Nonce").item(0).getTextContent(); | |||||
System.out.println(nonce); | |||||
String messageText = String.format(this.xmlFormat, cipherText); | |||||
System.out.println(messageText); | |||||
// 第三方收到企业号平台发送的消息 | |||||
String plainMessage = pc.decrypt(cipherText); | |||||
System.out.println(plainMessage); | |||||
assertEquals(plainMessage, this.replyMsg); | |||||
} | |||||
public void testAesEncrypt() { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
assertEquals(pc.encrypt(this.randomStr, this.replyMsg), this.afterAesEncrypt); | |||||
} | |||||
public void testAesEncrypt2() { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
assertEquals(pc.encrypt(this.randomStr, this.replyMsg2), this.afterAesEncrypt2); | |||||
} | |||||
public void testValidateSignatureError() throws ParserConfigurationException, SAXException, | |||||
IOException { | |||||
try { | |||||
WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); | |||||
String afterEncrpt = pc.encrypt(this.replyMsg); | |||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); | |||||
DocumentBuilder db = dbf.newDocumentBuilder(); | |||||
StringReader sr = new StringReader(afterEncrpt); | |||||
InputSource is = new InputSource(sr); | |||||
Document document = db.parse(is); | |||||
Element root = document.getDocumentElement(); | |||||
NodeList nodelist1 = root.getElementsByTagName("Encrypt"); | |||||
String encrypt = nodelist1.item(0).getTextContent(); | |||||
String fromXML = String.format(this.xmlFormat, encrypt); | |||||
pc.decrypt("12345", this.timestamp, this.nonce, fromXML); // 这里签名错误 | |||||
} catch (RuntimeException e) { | |||||
assertEquals(e.getMessage(), "加密消息签名校验失败"); | |||||
return; | |||||
} | |||||
fail("错误流程不抛出异常???"); | |||||
} | |||||
} |
@@ -2,8 +2,8 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import java.util.Map; | import java.util.Map; | ||||
@@ -2,7 +2,7 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import java.util.Map; | import java.util.Map; | ||||
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.api; | package me.chanjar.weixin.mp.api; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
/** | /** | ||||
* 消息匹配器,用在消息路由的时候 | * 消息匹配器,用在消息路由的时候 | ||||
@@ -1,230 +1,230 @@ | |||||
package me.chanjar.weixin.mp.api; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.concurrent.ExecutionException; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.Future; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
import me.chanjar.weixin.common.session.InternalSession; | |||||
import me.chanjar.weixin.common.session.InternalSessionManager; | |||||
import me.chanjar.weixin.common.session.StandardSessionManager; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | |||||
import me.chanjar.weixin.common.util.LogExceptionHandler; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
/** | |||||
* <pre> | |||||
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理 | |||||
* | |||||
* 说明: | |||||
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理 | |||||
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxMpMessageRouterRule#next()} | |||||
* 3. 规则的结束必须用{@link WxMpMessageRouterRule#end()}或者{@link WxMpMessageRouterRule#next()},否则不会生效 | |||||
* | |||||
* 使用方法: | |||||
* WxMpMessageRouter router = new WxMpMessageRouter(); | |||||
* router | |||||
* .rule() | |||||
* .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT") | |||||
* .interceptor(interceptor, ...).handler(handler, ...) | |||||
* .end() | |||||
* .rule() | |||||
* // 另外一个匹配规则 | |||||
* .end() | |||||
* ; | |||||
* | |||||
* // 将WxXmlMessage交给消息路由器 | |||||
* router.route(message); | |||||
* | |||||
* </pre> | |||||
* @author Daniel Qian | |||||
* | |||||
*/ | |||||
public class WxMpMessageRouter { | |||||
protected final Logger log = LoggerFactory.getLogger(WxMpMessageRouter.class); | |||||
private static final int DEFAULT_THREAD_POOL_SIZE = 100; | |||||
private final List<WxMpMessageRouterRule> rules = new ArrayList<>(); | |||||
private final WxMpService wxMpService; | |||||
private ExecutorService executorService; | |||||
private WxMessageDuplicateChecker messageDuplicateChecker; | |||||
private WxSessionManager sessionManager; | |||||
private WxErrorExceptionHandler exceptionHandler; | |||||
public WxMpMessageRouter(WxMpService wxMpService) { | |||||
this.wxMpService = wxMpService; | |||||
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||||
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); | |||||
this.sessionManager = new StandardSessionManager(); | |||||
this.exceptionHandler = new LogExceptionHandler(); | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的 {@link ExecutorService} | |||||
* 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100) | |||||
* </pre> | |||||
* @param executorService | |||||
*/ | |||||
public void setExecutorService(ExecutorService executorService) { | |||||
this.executorService = executorService; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | |||||
* </pre> | |||||
* @param messageDuplicateChecker | |||||
*/ | |||||
public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { | |||||
this.messageDuplicateChecker = messageDuplicateChecker; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} | |||||
* </pre> | |||||
* @param sessionManager | |||||
*/ | |||||
public void setSessionManager(WxSessionManager sessionManager) { | |||||
this.sessionManager = sessionManager; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | |||||
* </pre> | |||||
* @param exceptionHandler | |||||
*/ | |||||
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { | |||||
this.exceptionHandler = exceptionHandler; | |||||
} | |||||
List<WxMpMessageRouterRule> getRules() { | |||||
return this.rules; | |||||
} | |||||
/** | |||||
* 开始一个新的Route规则 | |||||
*/ | |||||
public WxMpMessageRouterRule rule() { | |||||
return new WxMpMessageRouterRule(this); | |||||
} | |||||
/** | |||||
* 处理微信消息 | |||||
* @param wxMessage | |||||
*/ | |||||
public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { | |||||
if (isDuplicateMessage(wxMessage)) { | |||||
// 如果是重复消息,那么就不做处理 | |||||
return null; | |||||
} | |||||
final List<WxMpMessageRouterRule> matchRules = new ArrayList<>(); | |||||
// 收集匹配的规则 | |||||
for (final WxMpMessageRouterRule rule : this.rules) { | |||||
if (rule.test(wxMessage)) { | |||||
matchRules.add(rule); | |||||
if(!rule.isReEnter()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (matchRules.size() == 0) { | |||||
return null; | |||||
} | |||||
WxMpXmlOutMessage res = null; | |||||
final List<Future<?>> futures = new ArrayList<>(); | |||||
for (final WxMpMessageRouterRule rule : matchRules) { | |||||
// 返回最后一个非异步的rule的执行结果 | |||||
if(rule.isAsync()) { | |||||
futures.add( | |||||
this.executorService.submit(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
rule.service(wxMessage, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); | |||||
} | |||||
}) | |||||
); | |||||
} else { | |||||
res = rule.service(wxMessage, this.wxMpService, this.sessionManager, this.exceptionHandler); | |||||
// 在同步操作结束,session访问结束 | |||||
this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); | |||||
sessionEndAccess(wxMessage); | |||||
} | |||||
} | |||||
if (futures.size() > 0) { | |||||
this.executorService.submit(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
for (Future<?> future : futures) { | |||||
try { | |||||
future.get(); | |||||
WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); | |||||
// 异步操作结束,session访问结束 | |||||
sessionEndAccess(wxMessage); | |||||
} catch (InterruptedException e) { | |||||
WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||||
} catch (ExecutionException e) { | |||||
WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
return res; | |||||
} | |||||
protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { | |||||
StringBuffer messageId = new StringBuffer(); | |||||
if (wxMessage.getMsgId() == null) { | |||||
messageId.append(wxMessage.getCreateTime()) | |||||
.append("-").append(wxMessage.getFromUser()) | |||||
.append("-").append(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
.append("-").append(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
; | |||||
} else { | |||||
messageId.append(wxMessage.getMsgId()); | |||||
} | |||||
return this.messageDuplicateChecker.isDuplicate(messageId.toString()); | |||||
} | |||||
/** | |||||
* 对session的访问结束 | |||||
* @param wxMessage | |||||
*/ | |||||
protected void sessionEndAccess(WxMpXmlMessage wxMessage) { | |||||
InternalSession session = ((InternalSessionManager)this.sessionManager).findSession(wxMessage.getFromUser()); | |||||
if (session != null) { | |||||
session.endAccess(); | |||||
} | |||||
} | |||||
} | |||||
package me.chanjar.weixin.mp.api; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.concurrent.ExecutionException; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.Future; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | |||||
import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; | |||||
import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; | |||||
import me.chanjar.weixin.common.session.InternalSession; | |||||
import me.chanjar.weixin.common.session.InternalSessionManager; | |||||
import me.chanjar.weixin.common.session.StandardSessionManager; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | |||||
import me.chanjar.weixin.common.util.LogExceptionHandler; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
/** | |||||
* <pre> | |||||
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理 | |||||
* | |||||
* 说明: | |||||
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理 | |||||
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxMpMessageRouterRule#next()} | |||||
* 3. 规则的结束必须用{@link WxMpMessageRouterRule#end()}或者{@link WxMpMessageRouterRule#next()},否则不会生效 | |||||
* | |||||
* 使用方法: | |||||
* WxMpMessageRouter router = new WxMpMessageRouter(); | |||||
* router | |||||
* .rule() | |||||
* .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT") | |||||
* .interceptor(interceptor, ...).handler(handler, ...) | |||||
* .end() | |||||
* .rule() | |||||
* // 另外一个匹配规则 | |||||
* .end() | |||||
* ; | |||||
* | |||||
* // 将WxXmlMessage交给消息路由器 | |||||
* router.route(message); | |||||
* | |||||
* </pre> | |||||
* @author Daniel Qian | |||||
* | |||||
*/ | |||||
public class WxMpMessageRouter { | |||||
protected final Logger log = LoggerFactory.getLogger(WxMpMessageRouter.class); | |||||
private static final int DEFAULT_THREAD_POOL_SIZE = 100; | |||||
private final List<WxMpMessageRouterRule> rules = new ArrayList<>(); | |||||
private final WxMpService wxMpService; | |||||
private ExecutorService executorService; | |||||
private WxMessageDuplicateChecker messageDuplicateChecker; | |||||
private WxSessionManager sessionManager; | |||||
private WxErrorExceptionHandler exceptionHandler; | |||||
public WxMpMessageRouter(WxMpService wxMpService) { | |||||
this.wxMpService = wxMpService; | |||||
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE); | |||||
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker(); | |||||
this.sessionManager = new StandardSessionManager(); | |||||
this.exceptionHandler = new LogExceptionHandler(); | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的 {@link ExecutorService} | |||||
* 如果不调用该方法,默认使用 Executors.newFixedThreadPool(100) | |||||
* </pre> | |||||
* @param executorService | |||||
*/ | |||||
public void setExecutorService(ExecutorService executorService) { | |||||
this.executorService = executorService; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的 {@link me.chanjar.weixin.common.api.WxMessageDuplicateChecker} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker} | |||||
* </pre> | |||||
* @param messageDuplicateChecker | |||||
*/ | |||||
public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) { | |||||
this.messageDuplicateChecker = messageDuplicateChecker; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的{@link me.chanjar.weixin.common.session.WxSessionManager} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.session.StandardSessionManager} | |||||
* </pre> | |||||
* @param sessionManager | |||||
*/ | |||||
public void setSessionManager(WxSessionManager sessionManager) { | |||||
this.sessionManager = sessionManager; | |||||
} | |||||
/** | |||||
* <pre> | |||||
* 设置自定义的{@link me.chanjar.weixin.common.api.WxErrorExceptionHandler} | |||||
* 如果不调用该方法,默认使用 {@link me.chanjar.weixin.common.util.LogExceptionHandler} | |||||
* </pre> | |||||
* @param exceptionHandler | |||||
*/ | |||||
public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) { | |||||
this.exceptionHandler = exceptionHandler; | |||||
} | |||||
List<WxMpMessageRouterRule> getRules() { | |||||
return this.rules; | |||||
} | |||||
/** | |||||
* 开始一个新的Route规则 | |||||
*/ | |||||
public WxMpMessageRouterRule rule() { | |||||
return new WxMpMessageRouterRule(this); | |||||
} | |||||
/** | |||||
* 处理微信消息 | |||||
* @param wxMessage | |||||
*/ | |||||
public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) { | |||||
if (isDuplicateMessage(wxMessage)) { | |||||
// 如果是重复消息,那么就不做处理 | |||||
return null; | |||||
} | |||||
final List<WxMpMessageRouterRule> matchRules = new ArrayList<>(); | |||||
// 收集匹配的规则 | |||||
for (final WxMpMessageRouterRule rule : this.rules) { | |||||
if (rule.test(wxMessage)) { | |||||
matchRules.add(rule); | |||||
if(!rule.isReEnter()) { | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (matchRules.size() == 0) { | |||||
return null; | |||||
} | |||||
WxMpXmlOutMessage res = null; | |||||
final List<Future<?>> futures = new ArrayList<>(); | |||||
for (final WxMpMessageRouterRule rule : matchRules) { | |||||
// 返回最后一个非异步的rule的执行结果 | |||||
if(rule.isAsync()) { | |||||
futures.add( | |||||
this.executorService.submit(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
rule.service(wxMessage, WxMpMessageRouter.this.wxMpService, WxMpMessageRouter.this.sessionManager, WxMpMessageRouter.this.exceptionHandler); | |||||
} | |||||
}) | |||||
); | |||||
} else { | |||||
res = rule.service(wxMessage, this.wxMpService, this.sessionManager, this.exceptionHandler); | |||||
// 在同步操作结束,session访问结束 | |||||
this.log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser()); | |||||
sessionEndAccess(wxMessage); | |||||
} | |||||
} | |||||
if (futures.size() > 0) { | |||||
this.executorService.submit(new Runnable() { | |||||
@Override | |||||
public void run() { | |||||
for (Future<?> future : futures) { | |||||
try { | |||||
future.get(); | |||||
WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser()); | |||||
// 异步操作结束,session访问结束 | |||||
sessionEndAccess(wxMessage); | |||||
} catch (InterruptedException e) { | |||||
WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||||
} catch (ExecutionException e) { | |||||
WxMpMessageRouter.this.log.error("Error happened when wait task finish", e); | |||||
} | |||||
} | |||||
} | |||||
}); | |||||
} | |||||
return res; | |||||
} | |||||
protected boolean isDuplicateMessage(WxMpXmlMessage wxMessage) { | |||||
StringBuffer messageId = new StringBuffer(); | |||||
if (wxMessage.getMsgId() == null) { | |||||
messageId.append(wxMessage.getCreateTime()) | |||||
.append("-").append(wxMessage.getFromUser()) | |||||
.append("-").append(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey()) | |||||
.append("-").append(wxMessage.getEvent() == null ? "" : wxMessage.getEvent()) | |||||
; | |||||
} else { | |||||
messageId.append(wxMessage.getMsgId()); | |||||
} | |||||
return this.messageDuplicateChecker.isDuplicate(messageId.toString()); | |||||
} | |||||
/** | |||||
* 对session的访问结束 | |||||
* @param wxMessage | |||||
*/ | |||||
protected void sessionEndAccess(WxMpXmlMessage wxMessage) { | |||||
InternalSession session = ((InternalSessionManager)this.sessionManager).findSession(wxMessage.getFromUser()); | |||||
if (session != null) { | |||||
session.endAccess(); | |||||
} | |||||
} | |||||
} |
@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | import me.chanjar.weixin.common.api.WxErrorExceptionHandler; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
@@ -4,14 +4,14 @@ import java.io.Serializable; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import me.chanjar.weixin.mp.bean.custombuilder.ImageBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.MpNewsBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.MusicBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.NewsBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.TextBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.VideoBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.VoiceBuilder; | |||||
import me.chanjar.weixin.mp.bean.custombuilder.WxCardBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.ImageBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.MpNewsBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.MusicBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.NewsBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.TextBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.VideoBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.VoiceBuilder; | |||||
import me.chanjar.weixin.mp.builder.kefu.WxCardBuilder; | |||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | ||||
/** | /** | ||||
@@ -1,22 +1,20 @@ | |||||
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 org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | import me.chanjar.weixin.mp.api.WxMpConfigStorage; | ||||
import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | ||||
import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | ||||
import org.apache.commons.io.IOUtils; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.Serializable; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | /** | ||||
* <pre> | * <pre> |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||||
public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = -2684778597067990723L; | private static final long serialVersionUID = -2684778597067990723L; | ||||
@XStreamAlias("Image") | @XStreamAlias("Image") |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | ||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | import me.chanjar.weixin.mp.api.WxMpConfigStorage; | ||||
import me.chanjar.weixin.mp.bean.outxmlbuilder.*; | |||||
import me.chanjar.weixin.mp.builder.outxml.*; | |||||
import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil; | ||||
import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | import me.chanjar.weixin.mp.util.xml.XStreamTransformer; | ||||
@@ -18,14 +18,14 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||||
@XStreamAlias("ToUserName") | @XStreamAlias("ToUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
protected String toUserName; | protected String toUserName; | ||||
@XStreamAlias("FromUserName") | @XStreamAlias("FromUserName") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
protected String fromUserName; | protected String fromUserName; | ||||
@XStreamAlias("CreateTime") | @XStreamAlias("CreateTime") | ||||
protected Long createTime; | protected Long createTime; | ||||
@XStreamAlias("MsgType") | @XStreamAlias("MsgType") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
protected String msgType; | protected String msgType; | ||||
@@ -61,7 +61,7 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||||
public void setMsgType(String msgType) { | public void setMsgType(String msgType) { | ||||
this.msgType = msgType; | this.msgType = msgType; | ||||
} | } | ||||
public String toXml() { | public String toXml() { | ||||
return XStreamTransformer.toXml((Class<WxMpXmlOutMessage>) this.getClass(), this); | return XStreamTransformer.toXml((Class<WxMpXmlOutMessage>) this.getClass(), this); | ||||
} | } | ||||
@@ -95,28 +95,28 @@ public abstract class WxMpXmlOutMessage implements Serializable { | |||||
public static VoiceBuilder VOICE() { | public static VoiceBuilder VOICE() { | ||||
return new VoiceBuilder(); | return new VoiceBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得视频消息builder | * 获得视频消息builder | ||||
*/ | */ | ||||
public static VideoBuilder VIDEO() { | public static VideoBuilder VIDEO() { | ||||
return new VideoBuilder(); | return new VideoBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得音乐消息builder | * 获得音乐消息builder | ||||
*/ | */ | ||||
public static MusicBuilder MUSIC() { | public static MusicBuilder MUSIC() { | ||||
return new MusicBuilder(); | return new MusicBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得图文消息builder | * 获得图文消息builder | ||||
*/ | */ | ||||
public static NewsBuilder NEWS() { | public static NewsBuilder NEWS() { | ||||
return new NewsBuilder(); | return new NewsBuilder(); | ||||
} | } | ||||
/** | /** | ||||
* 获得客服消息builder | * 获得客服消息builder | ||||
*/ | */ |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||||
public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = -4159937804975448945L; | private static final long serialVersionUID = -4159937804975448945L; | ||||
@XStreamAlias("Music") | @XStreamAlias("Music") | ||||
@@ -34,7 +34,7 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | |||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.music.setDescription(description); | this.music.setDescription(description); | ||||
} | } | ||||
public String getThumbMediaId() { | public String getThumbMediaId() { | ||||
return this.music.getThumbMediaId(); | return this.music.getThumbMediaId(); | ||||
} | } | ||||
@@ -58,10 +58,10 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | |||||
public void setHqMusicUrl(String hqMusicUrl) { | public void setHqMusicUrl(String hqMusicUrl) { | ||||
this.music.setHqMusicUrl(hqMusicUrl); | this.music.setHqMusicUrl(hqMusicUrl); | ||||
} | } | ||||
@XStreamAlias("Music") | @XStreamAlias("Music") | ||||
public static class Music { | public static class Music { | ||||
@XStreamAlias("Title") | @XStreamAlias("Title") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String title; | private String title; | ||||
@@ -73,15 +73,15 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | |||||
@XStreamAlias("ThumbMediaId") | @XStreamAlias("ThumbMediaId") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String thumbMediaId; | private String thumbMediaId; | ||||
@XStreamAlias("MusicUrl") | @XStreamAlias("MusicUrl") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String musicUrl; | private String musicUrl; | ||||
@XStreamAlias("HQMusicUrl") | @XStreamAlias("HQMusicUrl") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String hqMusicUrl; | private String hqMusicUrl; | ||||
public String getTitle() { | public String getTitle() { | ||||
return this.title; | return this.title; | ||||
} | } | ||||
@@ -121,7 +121,7 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage { | |||||
public void setHqMusicUrl(String hqMusicUrl) { | public void setHqMusicUrl(String hqMusicUrl) { | ||||
this.hqMusicUrl = hqMusicUrl; | this.hqMusicUrl = hqMusicUrl; | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -12,16 +12,16 @@ import java.util.List; | |||||
public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = -4604402850905714772L; | private static final long serialVersionUID = -4604402850905714772L; | ||||
@XStreamAlias("ArticleCount") | @XStreamAlias("ArticleCount") | ||||
protected int articleCount; | protected int articleCount; | ||||
@XStreamAlias("Articles") | @XStreamAlias("Articles") | ||||
protected final List<Item> articles = new ArrayList<>(); | protected final List<Item> articles = new ArrayList<>(); | ||||
public WxMpXmlOutNewsMessage() { | public WxMpXmlOutNewsMessage() { | ||||
this.msgType = WxConsts.XML_MSG_NEWS; | this.msgType = WxConsts.XML_MSG_NEWS; | ||||
} | } | ||||
@@ -34,15 +34,15 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | |||||
this.articles.add(item); | this.articles.add(item); | ||||
this.articleCount = this.articles.size(); | this.articleCount = this.articles.size(); | ||||
} | } | ||||
public List<Item> getArticles() { | public List<Item> getArticles() { | ||||
return this.articles; | return this.articles; | ||||
} | } | ||||
@XStreamAlias("item") | @XStreamAlias("item") | ||||
public static class Item { | public static class Item { | ||||
@XStreamAlias("Title") | @XStreamAlias("Title") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String Title; | private String Title; | ||||
@@ -54,11 +54,11 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage { | |||||
@XStreamAlias("PicUrl") | @XStreamAlias("PicUrl") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String PicUrl; | private String PicUrl; | ||||
@XStreamAlias("Url") | @XStreamAlias("Url") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String Url; | private String Url; | ||||
public String getTitle() { | public String getTitle() { | ||||
return this.Title; | return this.Title; | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -7,9 +7,9 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = -3972786455288763361L; | private static final long serialVersionUID = -3972786455288763361L; | ||||
@XStreamAlias("Content") | @XStreamAlias("Content") | ||||
@@ -19,7 +19,7 @@ public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage { | |||||
public WxMpXmlOutTextMessage() { | public WxMpXmlOutTextMessage() { | ||||
this.msgType = WxConsts.XML_MSG_TEXT; | this.msgType = WxConsts.XML_MSG_TEXT; | ||||
} | } | ||||
public String getContent() { | public String getContent() { | ||||
return this.content; | return this.content; | ||||
} | } | ||||
@@ -28,5 +28,5 @@ public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage { | |||||
this.content = content; | this.content = content; | ||||
} | } | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||||
public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = 1745902309380113978L; | private static final long serialVersionUID = 1745902309380113978L; | ||||
@XStreamAlias("Video") | @XStreamAlias("Video") | ||||
@@ -42,11 +42,11 @@ public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage { | |||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.video.setDescription(description); | this.video.setDescription(description); | ||||
} | } | ||||
@XStreamAlias("Video") | @XStreamAlias("Video") | ||||
public static class Video { | public static class Video { | ||||
@XStreamAlias("MediaId") | @XStreamAlias("MediaId") | ||||
@XStreamConverter(value=XStreamCDataConverter.class) | @XStreamConverter(value=XStreamCDataConverter.class) | ||||
private String mediaId; | private String mediaId; | ||||
@@ -82,7 +82,7 @@ public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage { | |||||
public void setDescription(String description) { | public void setDescription(String description) { | ||||
this.description = description; | this.description = description; | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | import com.thoughtworks.xstream.annotations.XStreamConverter; | ||||
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter; | |||||
public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage { | public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage { | ||||
/** | /** | ||||
* | |||||
* | |||||
*/ | */ | ||||
private static final long serialVersionUID = 240367390249860551L; | private static final long serialVersionUID = 240367390249860551L; | ||||
@XStreamAlias("Voice") | @XStreamAlias("Voice") |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | ||||
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.custombuilder; | |||||
package me.chanjar.weixin.mp.builder.kefu; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; |
@@ -1,29 +1,29 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
public abstract class BaseBuilder<BuilderType, ValueType> { | public abstract class BaseBuilder<BuilderType, ValueType> { | ||||
protected String toUserName; | protected String toUserName; | ||||
protected String fromUserName; | protected String fromUserName; | ||||
public BuilderType toUser(String touser) { | public BuilderType toUser(String touser) { | ||||
this.toUserName = touser; | this.toUserName = touser; | ||||
return (BuilderType) this; | return (BuilderType) this; | ||||
} | } | ||||
public BuilderType fromUser(String fromusername) { | public BuilderType fromUser(String fromusername) { | ||||
this.fromUserName = fromusername; | this.fromUserName = fromusername; | ||||
return (BuilderType) this; | return (BuilderType) this; | ||||
} | } | ||||
public abstract ValueType build(); | public abstract ValueType build(); | ||||
public void setCommon(WxMpXmlOutMessage m) { | public void setCommon(WxMpXmlOutMessage m) { | ||||
m.setToUserName(this.toUserName); | m.setToUserName(this.toUserName); | ||||
m.setFromUserName(this.fromUserName); | m.setFromUserName(this.fromUserName); | ||||
m.setCreateTime(System.currentTimeMillis() / 1000l); | m.setCreateTime(System.currentTimeMillis() / 1000l); | ||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | |||||
/** | /** | ||||
* 图片消息builder | * 图片消息builder | ||||
@@ -22,5 +22,5 @@ public final class ImageBuilder extends BaseBuilder<ImageBuilder, WxMpXmlOutImag | |||||
m.setMediaId(this.mediaId); | m.setMediaId(this.mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMusicMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessage; | |||||
/** | /** | ||||
* 音乐消息builder | * 音乐消息builder | ||||
* | |||||
* | |||||
* @author chanjarster | * @author chanjarster | ||||
*/ | */ | ||||
public final class MusicBuilder extends BaseBuilder<MusicBuilder, WxMpXmlOutMusicMessage> { | public final class MusicBuilder extends BaseBuilder<MusicBuilder, WxMpXmlOutMusicMessage> { |
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutNewsMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -12,12 +12,12 @@ import java.util.List; | |||||
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMessage> { | public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMessage> { | ||||
protected final List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<>(); | protected final List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<>(); | ||||
public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { | public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) { | ||||
this.articles.add(item); | this.articles.add(item); | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public WxMpXmlOutNewsMessage build() { | public WxMpXmlOutNewsMessage build() { | ||||
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | ||||
@@ -27,5 +27,5 @@ public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMe | |||||
setCommon(m); | setCommon(m); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; | |||||
/** | /** | ||||
* 文本消息builder | * 文本消息builder |
@@ -1,7 +1,7 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.common.util.StringUtils; | import me.chanjar.weixin.common.util.StringUtils; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTransferKefuMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; | |||||
/** | /** | ||||
* 客服消息builder | * 客服消息builder |
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutVideoMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessage; | |||||
/** | /** | ||||
* 视频消息builder | * 视频消息builder | ||||
@@ -25,7 +25,7 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxMpXmlOutVide | |||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public WxMpXmlOutVideoMessage build() { | public WxMpXmlOutVideoMessage build() { | ||||
WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage(); | WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage(); | ||||
@@ -35,5 +35,5 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxMpXmlOutVide | |||||
m.setMediaId(this.mediaId); | m.setMediaId(this.mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
package me.chanjar.weixin.mp.bean.outxmlbuilder; | |||||
package me.chanjar.weixin.mp.builder.outxml; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutVoiceMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessage; | |||||
/** | /** | ||||
* 语音消息builder | * 语音消息builder | ||||
@@ -14,7 +14,7 @@ public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxMpXmlOutVoic | |||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
return this; | return this; | ||||
} | } | ||||
@Override | @Override | ||||
public WxMpXmlOutVoiceMessage build() { | public WxMpXmlOutVoiceMessage build() { | ||||
WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage(); | WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage(); | ||||
@@ -22,5 +22,5 @@ public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxMpXmlOutVoic | |||||
m.setMediaId(this.mediaId); | m.setMediaId(this.mediaId); | ||||
return m; | return m; | ||||
} | } | ||||
} | } |
@@ -7,15 +7,15 @@ import java.util.Map; | |||||
import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMusicMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutNewsMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTextMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutTransferKefuMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutVideoMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutVoiceMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessage; | |||||
public class XStreamTransformer { | public class XStreamTransformer { | ||||
@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.common.session.StandardSessionManager; | import me.chanjar.weixin.common.session.StandardSessionManager; | ||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.DataProvider; | import org.testng.annotations.DataProvider; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@@ -18,7 +18,7 @@ import java.util.Map; | |||||
*/ | */ | ||||
@Test | @Test | ||||
public class WxMpMessageRouterTest { | public class WxMpMessageRouterTest { | ||||
@Test(enabled = false) | @Test(enabled = false) | ||||
public void prepare(boolean async, StringBuffer sb, WxMpMessageRouter router) { | public void prepare(boolean async, StringBuffer sb, WxMpMessageRouter router) { | ||||
router | router | ||||
@@ -50,7 +50,7 @@ public class WxMpMessageRouterTest { | |||||
}).handler(new WxEchoMpMessageHandler(sb, "matcher")).end() | }).handler(new WxEchoMpMessageHandler(sb, "matcher")).end() | ||||
.rule().async(async).handler(new WxEchoMpMessageHandler(sb, "ALL")).end(); | .rule().async(async).handler(new WxEchoMpMessageHandler(sb, "ALL")).end(); | ||||
} | } | ||||
@Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
public void testSync(WxMpXmlMessage message, String expected) { | public void testSync(WxMpXmlMessage message, String expected) { | ||||
StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
@@ -59,7 +59,7 @@ public class WxMpMessageRouterTest { | |||||
router.route(message); | router.route(message); | ||||
Assert.assertEquals(sb.toString(), expected); | Assert.assertEquals(sb.toString(), expected); | ||||
} | } | ||||
@Test(dataProvider="messages-1") | @Test(dataProvider="messages-1") | ||||
public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { | public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException { | ||||
StringBuffer sb = new StringBuffer(); | StringBuffer sb = new StringBuffer(); | ||||
@@ -69,7 +69,7 @@ public class WxMpMessageRouterTest { | |||||
Thread.sleep(500l); | Thread.sleep(500l); | ||||
Assert.assertEquals(sb.toString(), expected); | Assert.assertEquals(sb.toString(), expected); | ||||
} | } | ||||
public void testConcurrency() throws InterruptedException { | public void testConcurrency() throws InterruptedException { | ||||
final WxMpMessageRouter router = new WxMpMessageRouter(null); | final WxMpMessageRouter router = new WxMpMessageRouter(null); | ||||
router.rule().handler(new WxMpMessageHandler() { | router.rule().handler(new WxMpMessageHandler() { | ||||
@@ -79,7 +79,7 @@ public class WxMpMessageRouterTest { | |||||
return null; | return null; | ||||
} | } | ||||
}).end(); | }).end(); | ||||
final WxMpXmlMessage m = new WxMpXmlMessage(); | final WxMpXmlMessage m = new WxMpXmlMessage(); | ||||
Runnable r = new Runnable() { | Runnable r = new Runnable() { | ||||
@Override | @Override | ||||
@@ -94,26 +94,26 @@ public class WxMpMessageRouterTest { | |||||
for (int i = 0; i < 10; i++) { | for (int i = 0; i < 10; i++) { | ||||
new Thread(r).start(); | new Thread(r).start(); | ||||
} | } | ||||
Thread.sleep(1000l * 2); | Thread.sleep(1000l * 2); | ||||
} | } | ||||
@DataProvider(name="messages-1") | @DataProvider(name="messages-1") | ||||
public Object[][] messages2() { | public Object[][] messages2() { | ||||
WxMpXmlMessage message1 = new WxMpXmlMessage(); | WxMpXmlMessage message1 = new WxMpXmlMessage(); | ||||
message1.setMsgType(WxConsts.XML_MSG_TEXT); | message1.setMsgType(WxConsts.XML_MSG_TEXT); | ||||
WxMpXmlMessage message2 = new WxMpXmlMessage(); | WxMpXmlMessage message2 = new WxMpXmlMessage(); | ||||
message2.setEvent(WxConsts.EVT_CLICK); | message2.setEvent(WxConsts.EVT_CLICK); | ||||
WxMpXmlMessage message3 = new WxMpXmlMessage(); | WxMpXmlMessage message3 = new WxMpXmlMessage(); | ||||
message3.setEventKey("KEY_1"); | message3.setEventKey("KEY_1"); | ||||
WxMpXmlMessage message4 = new WxMpXmlMessage(); | WxMpXmlMessage message4 = new WxMpXmlMessage(); | ||||
message4.setContent("CONTENT_1"); | message4.setContent("CONTENT_1"); | ||||
WxMpXmlMessage message5 = new WxMpXmlMessage(); | WxMpXmlMessage message5 = new WxMpXmlMessage(); | ||||
message5.setContent("BLA"); | message5.setContent("BLA"); | ||||
WxMpXmlMessage message6 = new WxMpXmlMessage(); | WxMpXmlMessage message6 = new WxMpXmlMessage(); | ||||
message6.setContent("abcd"); | message6.setContent("abcd"); | ||||
@@ -123,18 +123,18 @@ public class WxMpMessageRouterTest { | |||||
WxMpXmlMessage c2 = new WxMpXmlMessage(); | WxMpXmlMessage c2 = new WxMpXmlMessage(); | ||||
c2.setMsgType(WxConsts.XML_MSG_TEXT); | c2.setMsgType(WxConsts.XML_MSG_TEXT); | ||||
c2.setEvent(WxConsts.EVT_CLICK); | c2.setEvent(WxConsts.EVT_CLICK); | ||||
WxMpXmlMessage c3 = new WxMpXmlMessage(); | WxMpXmlMessage c3 = new WxMpXmlMessage(); | ||||
c3.setMsgType(WxConsts.XML_MSG_TEXT); | c3.setMsgType(WxConsts.XML_MSG_TEXT); | ||||
c3.setEvent(WxConsts.EVT_CLICK); | c3.setEvent(WxConsts.EVT_CLICK); | ||||
c3.setEventKey("KEY_1"); | c3.setEventKey("KEY_1"); | ||||
WxMpXmlMessage c4 = new WxMpXmlMessage(); | WxMpXmlMessage c4 = new WxMpXmlMessage(); | ||||
c4.setMsgType(WxConsts.XML_MSG_TEXT); | c4.setMsgType(WxConsts.XML_MSG_TEXT); | ||||
c4.setEvent(WxConsts.EVT_CLICK); | c4.setEvent(WxConsts.EVT_CLICK); | ||||
c4.setEventKey("KEY_1"); | c4.setEventKey("KEY_1"); | ||||
c4.setContent("CONTENT_1"); | c4.setContent("CONTENT_1"); | ||||
return new Object[][] { | return new Object[][] { | ||||
new Object[] { message1, WxConsts.XML_MSG_TEXT + "," }, | new Object[] { message1, WxConsts.XML_MSG_TEXT + "," }, | ||||
new Object[] { message2, WxConsts.EVT_CLICK + "," }, | new Object[] { message2, WxConsts.EVT_CLICK + "," }, | ||||
@@ -147,7 +147,7 @@ public class WxMpMessageRouterTest { | |||||
new Object[] { c3, "COMBINE_3," }, | new Object[] { c3, "COMBINE_3," }, | ||||
new Object[] { c4, "COMBINE_4," } | new Object[] { c4, "COMBINE_4," } | ||||
}; | }; | ||||
} | } | ||||
public static class WxEchoMpMessageHandler implements WxMpMessageHandler { | public static class WxEchoMpMessageHandler implements WxMpMessageHandler { | ||||
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import org.testng.Assert; | import org.testng.Assert; |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@@ -12,7 +12,7 @@ public class WxMpXmlOutImageMessageTest { | |||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
m.setToUserName("to"); | m.setToUserName("to"); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
@@ -23,7 +23,7 @@ public class WxMpXmlOutImageMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
@@ -38,11 +38,11 @@ public class WxMpXmlOutImageMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@Test | @Test | ||||
public class WxXmlOutMusicMessageTest { | |||||
public class WxMpXmlOutMusicMessageTest { | |||||
public void test() { | public void test() { | ||||
WxMpXmlOutMusicMessage m = new WxMpXmlOutMusicMessage(); | WxMpXmlOutMusicMessage m = new WxMpXmlOutMusicMessage(); | ||||
@@ -16,7 +16,7 @@ public class WxXmlOutMusicMessageTest { | |||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
@@ -33,7 +33,7 @@ public class WxXmlOutMusicMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutMusicMessage m = WxMpXmlOutMessage.MUSIC() | WxMpXmlOutMusicMessage m = WxMpXmlOutMessage.MUSIC() | ||||
.fromUser("fromUser") | .fromUser("fromUser") | ||||
@@ -62,11 +62,11 @@ public class WxXmlOutMusicMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -1,17 +1,17 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@Test | @Test | ||||
public class WxXmlOutNewsMessageTest { | |||||
public class WxMpXmlOutNewsMessageTest { | |||||
public void test() { | public void test() { | ||||
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage(); | ||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | ||||
item.setDescription("description"); | item.setDescription("description"); | ||||
item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
@@ -43,14 +43,14 @@ public class WxXmlOutNewsMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item(); | ||||
item.setDescription("description"); | item.setDescription("description"); | ||||
item.setPicUrl("picUrl"); | item.setPicUrl("picUrl"); | ||||
item.setTitle("title"); | item.setTitle("title"); | ||||
item.setUrl("url"); | item.setUrl("url"); | ||||
WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() | WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS() | ||||
.fromUser("fromUser") | .fromUser("fromUser") | ||||
.toUser("toUser") | .toUser("toUser") | ||||
@@ -83,11 +83,11 @@ public class WxXmlOutNewsMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@Test | @Test | ||||
public class WxXmlOutTextMessageTest { | |||||
public class WxMpXmlOutTextMessageTest { | |||||
public void test() { | public void test() { | ||||
WxMpXmlOutTextMessage m = new WxMpXmlOutTextMessage(); | WxMpXmlOutTextMessage m = new WxMpXmlOutTextMessage(); | ||||
@@ -12,7 +12,7 @@ public class WxXmlOutTextMessageTest { | |||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
m.setToUserName("to"); | m.setToUserName("to"); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
@@ -23,7 +23,7 @@ public class WxXmlOutTextMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build(); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
@@ -38,13 +38,13 @@ public class WxXmlOutTextMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@Test | @Test | ||||
public class WxXmlOutVideoMessageTest { | |||||
public class WxMpXmlOutVideoMessageTest { | |||||
public void test() { | public void test() { | ||||
WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage(); | WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage(); | ||||
@@ -14,7 +14,7 @@ public class WxXmlOutVideoMessageTest { | |||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("fromUser"); | m.setFromUserName("fromUser"); | ||||
m.setToUserName("toUser"); | m.setToUserName("toUser"); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
+ "<ToUserName><![CDATA[toUser]]></ToUserName>" | + "<ToUserName><![CDATA[toUser]]></ToUserName>" | ||||
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>" | + "<FromUserName><![CDATA[fromUser]]></FromUserName>" | ||||
@@ -29,7 +29,7 @@ public class WxXmlOutVideoMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutVideoMessage m = WxMpXmlOutMessage.VIDEO() | WxMpXmlOutVideoMessage m = WxMpXmlOutMessage.VIDEO() | ||||
.mediaId("media_id") | .mediaId("media_id") | ||||
@@ -54,11 +54,11 @@ public class WxXmlOutVideoMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -1,10 +1,10 @@ | |||||
package me.chanjar.weixin.mp.bean; | |||||
package me.chanjar.weixin.mp.bean.message; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
@Test | @Test | ||||
public class WxXmlOutVoiceMessageTest { | |||||
public class WxMpXmlOutVoiceMessageTest { | |||||
public void test() { | public void test() { | ||||
WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage(); | WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage(); | ||||
@@ -12,7 +12,7 @@ public class WxXmlOutVoiceMessageTest { | |||||
m.setCreateTime(1122l); | m.setCreateTime(1122l); | ||||
m.setFromUserName("from"); | m.setFromUserName("from"); | ||||
m.setToUserName("to"); | m.setToUserName("to"); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
+ "<ToUserName><![CDATA[to]]></ToUserName>" | + "<ToUserName><![CDATA[to]]></ToUserName>" | ||||
+ "<FromUserName><![CDATA[from]]></FromUserName>" | + "<FromUserName><![CDATA[from]]></FromUserName>" | ||||
@@ -23,7 +23,7 @@ public class WxXmlOutVoiceMessageTest { | |||||
System.out.println(m.toXml()); | System.out.println(m.toXml()); | ||||
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", "")); | ||||
} | } | ||||
public void testBuild() { | public void testBuild() { | ||||
WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build(); | ||||
String expected = "<xml>" | String expected = "<xml>" | ||||
@@ -38,11 +38,11 @@ public class WxXmlOutVoiceMessageTest { | |||||
m | m | ||||
.toXml() | .toXml() | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
.replaceAll("<CreateTime>.*?</CreateTime>", ""), | |||||
expected | expected | ||||
.replaceAll("\\s", "") | .replaceAll("\\s", "") | ||||
.replaceAll("<CreateTime>.*?</CreateTime>", "") | .replaceAll("<CreateTime>.*?</CreateTime>", "") | ||||
); | ); | ||||
} | } | ||||
} | } |
@@ -11,8 +11,8 @@ import me.chanjar.weixin.mp.api.WxMpMessageHandler; | |||||
import me.chanjar.weixin.mp.api.WxMpMessageMatcher; | import me.chanjar.weixin.mp.api.WxMpMessageMatcher; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | import me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMatcher { | public class DemoGuessNumberHandler implements WxMpMessageHandler, WxMpMessageMatcher { | ||||
@@ -6,9 +6,9 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.bean.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessage; | |||||
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.Map; | import java.util.Map; | ||||
@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.demo; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | 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.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import java.util.Map; | import java.util.Map; | ||||
@@ -4,8 +4,8 @@ import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | 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.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import java.util.Map; | import java.util.Map; | ||||
@@ -3,9 +3,9 @@ package me.chanjar.weixin.mp.demo; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | 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.WxMpXmlOutTextMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; | |||||
import java.util.Map; | import java.util.Map; | ||||
@@ -10,8 +10,8 @@ import me.chanjar.weixin.common.util.StringUtils; | |||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | import me.chanjar.weixin.mp.api.WxMpConfigStorage; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter; | import me.chanjar.weixin.mp.api.WxMpMessageRouter; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | 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.message.WxMpXmlMessage; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||
/** | /** | ||||
* @author Daniel Qian | * @author Daniel Qian | ||||
@@ -18,13 +18,13 @@ | |||||
<test name="Bean_Test"> | <test name="Bean_Test"> | ||||
<classes> | <classes> | ||||
<class name="me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessageTest" /> | <class name="me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessageTest" /> | ||||
<class name="me.chanjar.weixin.mp.bean.WxMpXmlMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxMpXmlOutImageMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxXmlOutMusicMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxXmlOutNewsMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxXmlOutVideoMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxXmlOutVoiceMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.WxXmlOutTextMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessageTest" /> | |||||
<class name="me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessageTest" /> | |||||
</classes> | </classes> | ||||
</test> | </test> | ||||
</suite> | </suite> |