Bläddra i källkod

重构bean和builder的包结构

master
Binary Wang 8 år sedan
förälder
incheckning
44a1dfaf63
51 ändrade filer med 827 tillägg och 829 borttagningar
  1. +300
    -300
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java
  2. +103
    -103
      weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java
  3. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java
  4. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java
  5. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java
  6. +230
    -230
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java
  7. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java
  8. +8
    -8
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java
  9. +10
    -12
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
  10. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java
  11. +10
    -10
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java
  12. +9
    -9
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java
  13. +10
    -10
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java
  14. +5
    -5
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java
  15. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java
  16. +5
    -5
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java
  17. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java
  18. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java
  19. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java
  20. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java
  21. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java
  22. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java
  23. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java
  24. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java
  25. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java
  26. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java
  27. +8
    -8
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java
  28. +3
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java
  29. +3
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/MusicBuilder.java
  30. +5
    -5
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java
  31. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java
  32. +2
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java
  33. +4
    -4
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java
  34. +4
    -4
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java
  35. +9
    -9
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java
  36. +17
    -17
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java
  37. +1
    -1
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java
  38. +5
    -5
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java
  39. +6
    -6
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java
  40. +7
    -7
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java
  41. +7
    -7
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java
  42. +1
    -1
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessageTest.java
  43. +6
    -6
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java
  44. +6
    -6
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java
  45. +2
    -2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java
  46. +3
    -3
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java
  47. +2
    -2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoLogHandler.java
  48. +2
    -2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java
  49. +3
    -3
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoTextHandler.java
  50. +2
    -2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java
  51. +7
    -7
      weixin-java-mp/src/test/resources/testng.xml

+ 300
- 300
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java Visa fil

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

+ 103
- 103
weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java Visa fil

@@ -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
- 2
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageHandler.java Visa fil

@@ -2,8 +2,8 @@ package me.chanjar.weixin.mp.api;

import me.chanjar.weixin.common.exception.WxErrorException;
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;



+ 1
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageInterceptor.java Visa fil

@@ -2,7 +2,7 @@ package me.chanjar.weixin.mp.api;

import me.chanjar.weixin.common.exception.WxErrorException;
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;



+ 1
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageMatcher.java Visa fil

@@ -1,6 +1,6 @@
package me.chanjar.weixin.mp.api;

import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;

/**
* 消息匹配器,用在消息路由的时候


+ 230
- 230
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java Visa fil

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

+ 2
- 2
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouterRule.java Visa fil

@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
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.HashMap;


+ 8
- 8
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java Visa fil

@@ -4,14 +4,14 @@ import java.io.Serializable;
import java.util.ArrayList;
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;

/**


weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java Visa fil

@@ -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.XStreamConverter;

import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.util.crypto.WxMpCryptUtil;
import me.chanjar.weixin.mp.util.xml.XStreamTransformer;
import org.apache.commons.io.IOUtils;
import 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>

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
public class WxMpXmlOutImageMessage extends WxMpXmlOutMessage {

/**
*
*
*/
private static final long serialVersionUID = -2684778597067990723L;
@XStreamAlias("Image")

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMessage.java Visa fil

@@ -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.XStreamConverter;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
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.xml.XStreamTransformer;

@@ -18,14 +18,14 @@ public abstract class WxMpXmlOutMessage implements Serializable {
@XStreamAlias("ToUserName")
@XStreamConverter(value=XStreamCDataConverter.class)
protected String toUserName;
@XStreamAlias("FromUserName")
@XStreamConverter(value=XStreamCDataConverter.class)
protected String fromUserName;
@XStreamAlias("CreateTime")
protected Long createTime;
@XStreamAlias("MsgType")
@XStreamConverter(value=XStreamCDataConverter.class)
protected String msgType;
@@ -61,7 +61,7 @@ public abstract class WxMpXmlOutMessage implements Serializable {
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String toXml() {
return XStreamTransformer.toXml((Class<WxMpXmlOutMessage>) this.getClass(), this);
}
@@ -95,28 +95,28 @@ public abstract class WxMpXmlOutMessage implements Serializable {
public static VoiceBuilder VOICE() {
return new VoiceBuilder();
}
/**
* 获得视频消息builder
*/
public static VideoBuilder VIDEO() {
return new VideoBuilder();
}
/**
* 获得音乐消息builder
*/
public static MusicBuilder MUSIC() {
return new MusicBuilder();
}
/**
* 获得图文消息builder
*/
public static NewsBuilder NEWS() {
return new NewsBuilder();
}
/**
* 获得客服消息builder
*/

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutMusicMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {

/**
*
*
*/
private static final long serialVersionUID = -4159937804975448945L;
@XStreamAlias("Music")
@@ -34,7 +34,7 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
public void setDescription(String description) {
this.music.setDescription(description);
}
public String getThumbMediaId() {
return this.music.getThumbMediaId();
}
@@ -58,10 +58,10 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
public void setHqMusicUrl(String hqMusicUrl) {
this.music.setHqMusicUrl(hqMusicUrl);
}
@XStreamAlias("Music")
public static class Music {
@XStreamAlias("Title")
@XStreamConverter(value=XStreamCDataConverter.class)
private String title;
@@ -73,15 +73,15 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
@XStreamAlias("ThumbMediaId")
@XStreamConverter(value=XStreamCDataConverter.class)
private String thumbMediaId;
@XStreamAlias("MusicUrl")
@XStreamConverter(value=XStreamCDataConverter.class)
private String musicUrl;
@XStreamAlias("HQMusicUrl")
@XStreamConverter(value=XStreamCDataConverter.class)
private String hqMusicUrl;
public String getTitle() {
return this.title;
}
@@ -121,7 +121,7 @@ public class WxMpXmlOutMusicMessage extends WxMpXmlOutMessage {
public void setHqMusicUrl(String hqMusicUrl) {
this.hqMusicUrl = hqMusicUrl;
}
}

}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutNewsMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -12,16 +12,16 @@ import java.util.List;
public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {

/**
*
*
*/
private static final long serialVersionUID = -4604402850905714772L;

@XStreamAlias("ArticleCount")
protected int articleCount;
@XStreamAlias("Articles")
protected final List<Item> articles = new ArrayList<>();
public WxMpXmlOutNewsMessage() {
this.msgType = WxConsts.XML_MSG_NEWS;
}
@@ -34,15 +34,15 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
this.articles.add(item);
this.articleCount = this.articles.size();
}
public List<Item> getArticles() {
return this.articles;
}
@XStreamAlias("item")
public static class Item {
@XStreamAlias("Title")
@XStreamConverter(value=XStreamCDataConverter.class)
private String Title;
@@ -54,11 +54,11 @@ public class WxMpXmlOutNewsMessage extends WxMpXmlOutMessage {
@XStreamAlias("PicUrl")
@XStreamConverter(value=XStreamCDataConverter.class)
private String PicUrl;
@XStreamAlias("Url")
@XStreamConverter(value=XStreamCDataConverter.class)
private String Url;
public String getTitle() {
return this.Title;
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTextMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -7,9 +7,9 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;

@XStreamAlias("xml")
public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage {
/**
*
*
*/
private static final long serialVersionUID = -3972786455288763361L;
@XStreamAlias("Content")
@@ -19,7 +19,7 @@ public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage {
public WxMpXmlOutTextMessage() {
this.msgType = WxConsts.XML_MSG_TEXT;
}
public String getContent() {
return this.content;
}
@@ -28,5 +28,5 @@ public class WxMpXmlOutTextMessage extends WxMpXmlOutMessage {
this.content = content;
}

}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferKefuMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessage.java Visa fil

@@ -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.XStreamConverter;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVideoMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {

/**
*
*
*/
private static final long serialVersionUID = 1745902309380113978L;
@XStreamAlias("Video")
@@ -42,11 +42,11 @@ public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {
public void setDescription(String description) {
this.video.setDescription(description);
}

@XStreamAlias("Video")
public static class Video {
@XStreamAlias("MediaId")
@XStreamConverter(value=XStreamCDataConverter.class)
private String mediaId;
@@ -82,7 +82,7 @@ public class WxMpXmlOutVideoMessage extends WxMpXmlOutMessage {
public void setDescription(String description) {
this.description = description;
}
}

}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpXmlOutVoiceMessage.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessage.java Visa fil

@@ -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.XStreamConverter;
@@ -9,7 +9,7 @@ import me.chanjar.weixin.common.util.xml.XStreamMediaIdConverter;
public class WxMpXmlOutVoiceMessage extends WxMpXmlOutMessage {

/**
*
*
*/
private static final long serialVersionUID = 240367390249860551L;
@XStreamAlias("Voice")

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/BaseBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/BaseBuilder.java Visa fil

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


weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/ImageBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/ImageBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/MpNewsBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MpNewsBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/MusicBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/MusicBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/NewsBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/NewsBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/TextBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/TextBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VideoBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VideoBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/VoiceBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/VoiceBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/custombuilder/WxCardBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/kefu/WxCardBuilder.java Visa fil

@@ -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.mp.bean.kefu.WxMpKefuMessage;

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/BaseBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/BaseBuilder.java Visa fil

@@ -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> {
protected String toUserName;
protected String fromUserName;
public BuilderType toUser(String touser) {
this.toUserName = touser;
return (BuilderType) this;
}
public BuilderType fromUser(String fromusername) {
this.fromUserName = fromusername;
return (BuilderType) this;
}

public abstract ValueType build();
public void setCommon(WxMpXmlOutMessage m) {
m.setToUserName(this.toUserName);
m.setFromUserName(this.fromUserName);
m.setCreateTime(System.currentTimeMillis() / 1000l);
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/ImageBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/ImageBuilder.java Visa fil

@@ -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
@@ -22,5 +22,5 @@ public final class ImageBuilder extends BaseBuilder<ImageBuilder, WxMpXmlOutImag
m.setMediaId(this.mediaId);
return m;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/MusicBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/MusicBuilder.java Visa fil

@@ -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
*
*
* @author chanjarster
*/
public final class MusicBuilder extends BaseBuilder<MusicBuilder, WxMpXmlOutMusicMessage> {

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/NewsBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/NewsBuilder.java Visa fil

@@ -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.List;
@@ -12,12 +12,12 @@ import java.util.List;
public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMessage> {

protected final List<WxMpXmlOutNewsMessage.Item> articles = new ArrayList<>();
public NewsBuilder addArticle(WxMpXmlOutNewsMessage.Item item) {
this.articles.add(item);
return this;
}
@Override
public WxMpXmlOutNewsMessage build() {
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage();
@@ -27,5 +27,5 @@ public final class NewsBuilder extends BaseBuilder<NewsBuilder, WxMpXmlOutNewsMe
setCommon(m);
return m;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TextBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TextBuilder.java Visa fil

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

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/TransferCustomerServiceBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/TransferCustomerServiceBuilder.java Visa fil

@@ -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.mp.bean.WxMpXmlOutTransferKefuMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage;

/**
* 客服消息builder

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VideoBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VideoBuilder.java Visa fil

@@ -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
@@ -25,7 +25,7 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxMpXmlOutVide
this.mediaId = mediaId;
return this;
}
@Override
public WxMpXmlOutVideoMessage build() {
WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage();
@@ -35,5 +35,5 @@ public final class VideoBuilder extends BaseBuilder<VideoBuilder, WxMpXmlOutVide
m.setMediaId(this.mediaId);
return m;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/outxmlbuilder/VoiceBuilder.java → weixin-java-mp/src/main/java/me/chanjar/weixin/mp/builder/outxml/VoiceBuilder.java Visa fil

@@ -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
@@ -14,7 +14,7 @@ public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxMpXmlOutVoic
this.mediaId = mediaId;
return this;
}
@Override
public WxMpXmlOutVoiceMessage build() {
WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage();
@@ -22,5 +22,5 @@ public final class VoiceBuilder extends BaseBuilder<VoiceBuilder, WxMpXmlOutVoic
m.setMediaId(this.mediaId);
return m;
}
}

+ 9
- 9
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java Visa fil

@@ -7,15 +7,15 @@ import java.util.Map;
import com.thoughtworks.xstream.XStream;

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 {



+ 17
- 17
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMessageRouterTest.java Visa fil

@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.api;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.StandardSessionManager;
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.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -18,7 +18,7 @@ import java.util.Map;
*/
@Test
public class WxMpMessageRouterTest {
@Test(enabled = false)
public void prepare(boolean async, StringBuffer sb, WxMpMessageRouter router) {
router
@@ -50,7 +50,7 @@ public class WxMpMessageRouterTest {
}).handler(new WxEchoMpMessageHandler(sb, "matcher")).end()
.rule().async(async).handler(new WxEchoMpMessageHandler(sb, "ALL")).end();
}
@Test(dataProvider="messages-1")
public void testSync(WxMpXmlMessage message, String expected) {
StringBuffer sb = new StringBuffer();
@@ -59,7 +59,7 @@ public class WxMpMessageRouterTest {
router.route(message);
Assert.assertEquals(sb.toString(), expected);
}
@Test(dataProvider="messages-1")
public void testAsync(WxMpXmlMessage message, String expected) throws InterruptedException {
StringBuffer sb = new StringBuffer();
@@ -69,7 +69,7 @@ public class WxMpMessageRouterTest {
Thread.sleep(500l);
Assert.assertEquals(sb.toString(), expected);
}
public void testConcurrency() throws InterruptedException {
final WxMpMessageRouter router = new WxMpMessageRouter(null);
router.rule().handler(new WxMpMessageHandler() {
@@ -79,7 +79,7 @@ public class WxMpMessageRouterTest {
return null;
}
}).end();
final WxMpXmlMessage m = new WxMpXmlMessage();
Runnable r = new Runnable() {
@Override
@@ -94,26 +94,26 @@ public class WxMpMessageRouterTest {
for (int i = 0; i < 10; i++) {
new Thread(r).start();
}
Thread.sleep(1000l * 2);
}
@DataProvider(name="messages-1")
public Object[][] messages2() {
WxMpXmlMessage message1 = new WxMpXmlMessage();
message1.setMsgType(WxConsts.XML_MSG_TEXT);
WxMpXmlMessage message2 = new WxMpXmlMessage();
message2.setEvent(WxConsts.EVT_CLICK);
WxMpXmlMessage message3 = new WxMpXmlMessage();
message3.setEventKey("KEY_1");
WxMpXmlMessage message4 = new WxMpXmlMessage();
message4.setContent("CONTENT_1");
WxMpXmlMessage message5 = new WxMpXmlMessage();
message5.setContent("BLA");
WxMpXmlMessage message6 = new WxMpXmlMessage();
message6.setContent("abcd");

@@ -123,18 +123,18 @@ public class WxMpMessageRouterTest {
WxMpXmlMessage c2 = new WxMpXmlMessage();
c2.setMsgType(WxConsts.XML_MSG_TEXT);
c2.setEvent(WxConsts.EVT_CLICK);
WxMpXmlMessage c3 = new WxMpXmlMessage();
c3.setMsgType(WxConsts.XML_MSG_TEXT);
c3.setEvent(WxConsts.EVT_CLICK);
c3.setEventKey("KEY_1");
WxMpXmlMessage c4 = new WxMpXmlMessage();
c4.setMsgType(WxConsts.XML_MSG_TEXT);
c4.setEvent(WxConsts.EVT_CLICK);
c4.setEventKey("KEY_1");
c4.setContent("CONTENT_1");
return new Object[][] {
new Object[] { message1, WxConsts.XML_MSG_TEXT + "," },
new Object[] { message2, WxConsts.EVT_CLICK + "," },
@@ -147,7 +147,7 @@ public class WxMpMessageRouterTest {
new Object[] { c3, "COMBINE_3," },
new Object[] { c4, "COMBINE_4," }
};
}

public static class WxEchoMpMessageHandler implements WxMpMessageHandler {


weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java Visa fil

@@ -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 org.testng.Assert;

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutImageMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutImageMessageTest.java Visa fil

@@ -1,4 +1,4 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;
@@ -12,7 +12,7 @@ public class WxMpXmlOutImageMessageTest {
m.setCreateTime(1122l);
m.setFromUserName("from");
m.setToUserName("to");
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>"
@@ -23,7 +23,7 @@ public class WxMpXmlOutImageMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutImageMessage m = WxMpXmlOutMessage.IMAGE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build();
String expected = "<xml>"
@@ -38,11 +38,11 @@ public class WxMpXmlOutImageMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutMusicMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutMusicMessageTest.java Visa fil

@@ -1,10 +1,10 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

@Test
public class WxXmlOutMusicMessageTest {
public class WxMpXmlOutMusicMessageTest {

public void test() {
WxMpXmlOutMusicMessage m = new WxMpXmlOutMusicMessage();
@@ -16,7 +16,7 @@ public class WxXmlOutMusicMessageTest {
m.setCreateTime(1122l);
m.setFromUserName("fromUser");
m.setToUserName("toUser");
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>"
@@ -33,7 +33,7 @@ public class WxXmlOutMusicMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutMusicMessage m = WxMpXmlOutMessage.MUSIC()
.fromUser("fromUser")
@@ -62,11 +62,11 @@ public class WxXmlOutMusicMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutNewsMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutNewsMessageTest.java Visa fil

@@ -1,17 +1,17 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

@Test
public class WxXmlOutNewsMessageTest {
public class WxMpXmlOutNewsMessageTest {

public void test() {
WxMpXmlOutNewsMessage m = new WxMpXmlOutNewsMessage();
m.setCreateTime(1122l);
m.setFromUserName("fromUser");
m.setToUserName("toUser");
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item();
item.setDescription("description");
item.setPicUrl("picUrl");
@@ -43,14 +43,14 @@ public class WxXmlOutNewsMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutNewsMessage.Item item = new WxMpXmlOutNewsMessage.Item();
item.setDescription("description");
item.setPicUrl("picUrl");
item.setTitle("title");
item.setUrl("url");
WxMpXmlOutNewsMessage m = WxMpXmlOutMessage.NEWS()
.fromUser("fromUser")
.toUser("toUser")
@@ -83,11 +83,11 @@ public class WxXmlOutNewsMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutTextMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTextMessageTest.java Visa fil

@@ -1,10 +1,10 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

@Test
public class WxXmlOutTextMessageTest {
public class WxMpXmlOutTextMessageTest {

public void test() {
WxMpXmlOutTextMessage m = new WxMpXmlOutTextMessage();
@@ -12,7 +12,7 @@ public class WxXmlOutTextMessageTest {
m.setCreateTime(1122l);
m.setFromUserName("from");
m.setToUserName("to");
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>"
@@ -23,7 +23,7 @@ public class WxXmlOutTextMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutTextMessage m = WxMpXmlOutMessage.TEXT().content("content").fromUser("from").toUser("to").build();
String expected = "<xml>"
@@ -38,13 +38,13 @@ public class WxXmlOutTextMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}

}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxMpXmlOutTransferKefuMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutTransferKefuMessageTest.java Visa fil

@@ -1,4 +1,4 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVideoMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVideoMessageTest.java Visa fil

@@ -1,10 +1,10 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

@Test
public class WxXmlOutVideoMessageTest {
public class WxMpXmlOutVideoMessageTest {

public void test() {
WxMpXmlOutVideoMessage m = new WxMpXmlOutVideoMessage();
@@ -14,7 +14,7 @@ public class WxXmlOutVideoMessageTest {
m.setCreateTime(1122l);
m.setFromUserName("fromUser");
m.setToUserName("toUser");
String expected = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>"
@@ -29,7 +29,7 @@ public class WxXmlOutVideoMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutVideoMessage m = WxMpXmlOutMessage.VIDEO()
.mediaId("media_id")
@@ -54,11 +54,11 @@ public class WxXmlOutVideoMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/WxXmlOutVoiceMessageTest.java → weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlOutVoiceMessageTest.java Visa fil

@@ -1,10 +1,10 @@
package me.chanjar.weixin.mp.bean;
package me.chanjar.weixin.mp.bean.message;

import org.testng.Assert;
import org.testng.annotations.Test;

@Test
public class WxXmlOutVoiceMessageTest {
public class WxMpXmlOutVoiceMessageTest {

public void test() {
WxMpXmlOutVoiceMessage m = new WxMpXmlOutVoiceMessage();
@@ -12,7 +12,7 @@ public class WxXmlOutVoiceMessageTest {
m.setCreateTime(1122l);
m.setFromUserName("from");
m.setToUserName("to");
String expected = "<xml>"
+ "<ToUserName><![CDATA[to]]></ToUserName>"
+ "<FromUserName><![CDATA[from]]></FromUserName>"
@@ -23,7 +23,7 @@ public class WxXmlOutVoiceMessageTest {
System.out.println(m.toXml());
Assert.assertEquals(m.toXml().replaceAll("\\s", ""), expected.replaceAll("\\s", ""));
}
public void testBuild() {
WxMpXmlOutVoiceMessage m = WxMpXmlOutMessage.VOICE().mediaId("ddfefesfsdfef").fromUser("from").toUser("to").build();
String expected = "<xml>"
@@ -38,11 +38,11 @@ public class WxXmlOutVoiceMessageTest {
m
.toXml()
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
.replaceAll("<CreateTime>.*?</CreateTime>", ""),
expected
.replaceAll("\\s", "")
.replaceAll("<CreateTime>.*?</CreateTime>", "")
);
}
}

+ 2
- 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoGuessNumberHandler.java Visa fil

@@ -11,8 +11,8 @@ import me.chanjar.weixin.mp.api.WxMpMessageHandler;
import me.chanjar.weixin.mp.api.WxMpMessageMatcher;
import me.chanjar.weixin.mp.api.WxMpService;
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 {



+ 3
- 3
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java Visa fil

@@ -6,9 +6,9 @@ import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
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.util.Map;


+ 2
- 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoLogHandler.java Visa fil

@@ -3,8 +3,8 @@ package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
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;



+ 2
- 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoOAuth2Handler.java Visa fil

@@ -4,8 +4,8 @@ import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
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;



+ 3
- 3
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoTextHandler.java Visa fil

@@ -3,9 +3,9 @@ package me.chanjar.weixin.mp.demo;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpMessageHandler;
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;



+ 2
- 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpEndpointServlet.java Visa fil

@@ -10,8 +10,8 @@ import me.chanjar.weixin.common.util.StringUtils;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
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


+ 7
- 7
weixin-java-mp/src/test/resources/testng.xml Visa fil

@@ -18,13 +18,13 @@
<test name="Bean_Test">
<classes>
<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>
</test>
</suite>

Laddar…
Avbryt
Spara