Bläddra i källkod

Merge pull request #10 from Wechat-Group/develop

同步
Jonk 8 år sedan
förälder
incheckning
dc821ff00e
24 ändrade filer med 956 tillägg och 182 borttagningar
  1. 18 4
      .travis.yml
  2. 4 4
      README.md
  3. 1 2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java
  4. 70 42
      weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java
  5. 16 6
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
  6. 7 7
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java
  7. 80 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java
  8. 7 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
  9. 72 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java
  10. 15 2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
  11. 12 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java
  12. 60 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java
  13. 30 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java
  14. 53 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java
  15. 117 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java
  16. 47 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java
  17. 24 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java
  18. 38 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java
  19. 24 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java
  20. 54 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java
  21. 54 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java
  22. 9 11
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java
  23. 40 0
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java
  24. 104 104
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java

+ 18 - 4
.travis.yml

@@ -1,12 +1,26 @@
 language: java
-jdk:
-  - oraclejdk7
-
-script: "mvn clean package -Dmaven.test.skip=true"
+sudo: false
+install: true
+addons:
+  sonarqube:
+    token:
+      secure: "834110c7191f97ecb226970c46dcaff8e681da5a"
 
+jdk:
+  - oraclejdk8
+#script: "mvn clean package -Dmaven.test.skip=true"
+    
+script:
+  - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar
+  
 branches:
   only:
     - develop
+  
+cache:
+  directories:
+    - '$HOME/.m2/repository'
+    - '$HOME/.sonar/cache'
 
 notifications:
   email:

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4 - 4
README.md


+ 1 - 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/result/WxError.java

@@ -20,8 +20,7 @@ public class WxError implements Serializable {
   private String json;
 
   public static WxError fromJson(String json) {
-    WxError error = WxGsonBuilder.create().fromJson(json, WxError.class);
-    return error;
+    return WxGsonBuilder.create().fromJson(json, WxError.class);
   }
 
   public static Builder newBuilder() {

+ 70 - 42
weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSession.java

@@ -11,12 +11,12 @@ public class StandardSession implements WxSession, InternalSession {
   /**
    * The string manager for this package.
    */
-  protected static final StringManager sm =
-          StringManager.getManager(Constants.Package);
+  protected static final StringManager sm = StringManager.getManager(Constants.Package);
   /**
    * Type array.
    */
-  protected static final String EMPTY_ARRAY[] = new String[0];
+  private static final String[] EMPTY_ARRAY = new String[0];
+
   // ------------------------------ WxSession
   protected Map<String, Object> attributes = new ConcurrentHashMap<>();
   /**
@@ -71,20 +71,23 @@ public class StandardSession implements WxSession, InternalSession {
   @Override
   public Object getAttribute(String name) {
 
-    if (!isValidInternal())
+    if (!isValidInternal()) {
       throw new IllegalStateException
-              (sm.getString("sessionImpl.getAttribute.ise"));
+        (sm.getString("sessionImpl.getAttribute.ise"));
+    }
 
-    if (name == null) return null;
+    if (name == null) {
+      return null;
+    }
 
-    return (this.attributes.get(name));
+    return this.attributes.get(name);
   }
 
   @Override
   public Enumeration<String> getAttributeNames() {
-    if (!isValidInternal())
-      throw new IllegalStateException
-              (sm.getString("sessionImpl.getAttributeNames.ise"));
+    if (!isValidInternal()) {
+      throw new IllegalStateException(sm.getString("sessionImpl.getAttributeNames.ise"));
+    }
 
     Set<String> names = new HashSet<>();
     names.addAll(this.attributes.keySet());
@@ -94,9 +97,9 @@ public class StandardSession implements WxSession, InternalSession {
   @Override
   public void setAttribute(String name, Object value) {
     // Name cannot be null
-    if (name == null)
-      throw new IllegalArgumentException
-              (sm.getString("sessionImpl.setAttribute.namenull"));
+    if (name == null) {
+      throw new IllegalArgumentException(sm.getString("sessionImpl.setAttribute.namenull"));
+    }
 
     // Null value is the same as removeAttribute()
     if (value == null) {
@@ -105,9 +108,9 @@ public class StandardSession implements WxSession, InternalSession {
     }
 
     // Validate our current state
-    if (!isValidInternal())
-      throw new IllegalStateException(sm.getString(
-              "sessionImpl.setAttribute.ise", getIdInternal()));
+    if (!isValidInternal()) {
+      throw new IllegalStateException(sm.getString("sessionImpl.setAttribute.ise", getIdInternal()));
+    }
 
     this.attributes.put(name, value);
 
@@ -121,8 +124,7 @@ public class StandardSession implements WxSession, InternalSession {
   @Override
   public void invalidate() {
     if (!isValidInternal())
-      throw new IllegalStateException
-              (sm.getString("sessionImpl.invalidate.ise"));
+      throw new IllegalStateException(sm.getString("sessionImpl.invalidate.ise"));
 
     // Cause this session to expire
     expire();
@@ -131,12 +133,11 @@ public class StandardSession implements WxSession, InternalSession {
 
   @Override
   public WxSession getSession() {
-
     if (this.facade == null) {
       this.facade = new StandardSessionFacade(this);
     }
-    return (this.facade);
 
+    return this.facade;
   }
 
   /**
@@ -185,12 +186,14 @@ public class StandardSession implements WxSession, InternalSession {
 
   @Override
   public String getIdInternal() {
-    return (this.id);
+    return this.id;
   }
 
   protected void removeAttributeInternal(String name) {
     // Avoid NPE
-    if (name == null) return;
+    if (name == null) {
+      return;
+    }
 
     // Remove this attribute from our collection
     this.attributes.remove(name);
@@ -202,19 +205,22 @@ public class StandardSession implements WxSession, InternalSession {
     // Check to see if session has already been invalidated.
     // Do not check expiring at this point as expire should not return until
     // isValid is false
-    if (!this.isValid)
+    if (!this.isValid) {
       return;
+    }
 
     synchronized (this) {
       // Check again, now we are inside the sync so this code only runs once
       // Double check locking - isValid needs to be volatile
       // The check of expiring is to ensure that an infinite loop is not
       // entered as per bug 56339
-      if (this.expiring || !this.isValid)
+      if (this.expiring || !this.isValid) {
         return;
+      }
 
-      if (this.manager == null)
+      if (this.manager == null) {
         return;
+      }
 
       // Mark this session as "being expired"
       this.expiring = true;
@@ -230,9 +236,9 @@ public class StandardSession implements WxSession, InternalSession {
       this.expiring = false;
 
       // Unbind any objects associated with this session
-      String keys[] = keys();
-      for (int i = 0; i < keys.length; i++) {
-        removeAttributeInternal(keys[i]);
+      String[] keys = keys();
+      for (String key : keys) {
+        removeAttributeInternal(key);
       }
     }
 
@@ -273,13 +279,15 @@ public class StandardSession implements WxSession, InternalSession {
 
   @Override
   public void setId(String id) {
-    if ((this.id != null) && (this.manager != null))
+    if ((this.id != null) && (this.manager != null)) {
       this.manager.remove(this);
+    }
 
     this.id = id;
 
-    if (this.manager != null)
+    if (this.manager != null) {
       this.manager.add(this);
+    }
   }
 
   /**
@@ -295,21 +303,41 @@ public class StandardSession implements WxSession, InternalSession {
 
   @Override
   public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof StandardSession)) return false;
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof StandardSession)) {
+      return false;
+    }
 
     StandardSession session = (StandardSession) o;
 
-    if (this.creationTime != session.creationTime) return false;
-    if (this.expiring != session.expiring) return false;
-    if (this.isValid != session.isValid) return false;
-    if (this.maxInactiveInterval != session.maxInactiveInterval) return false;
-    if (this.thisAccessedTime != session.thisAccessedTime) return false;
-    if (!this.accessCount.equals(session.accessCount)) return false;
-    if (!this.attributes.equals(session.attributes)) return false;
-    if (!this.facade.equals(session.facade)) return false;
-    if (!this.id.equals(session.id)) return false;
-    return this.manager.equals(session.manager);
+    if (this.creationTime != session.creationTime) {
+      return false;
+    }
+    if (this.expiring != session.expiring) {
+      return false;
+    }
+    if (this.isValid != session.isValid) {
+      return false;
+    }
+    if (this.maxInactiveInterval != session.maxInactiveInterval) {
+      return false;
+    }
+    if (this.thisAccessedTime != session.thisAccessedTime) {
+      return false;
+    }
+    if (this.accessCount.get() != session.accessCount.get()) {
+      return false;
+    }
+    if (!this.attributes.equals(session.attributes)) {
+      return false;
+    }
+    if (!this.facade.equals(session.facade)) {
+      return false;
+    }
+
+    return this.id.equals(session.id) && this.manager.equals(session.manager);
 
   }
 

+ 16 - 6
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java

@@ -538,8 +538,16 @@ public class WxCpServiceImpl implements WxCpService {
     int retryTimes = 0;
     do {
       try {
-        return executeInternal(executor, uri, data);
+        T result = this.executeInternal(executor, uri, data);
+        this.log.debug("\n[URL]:  {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result);
+        return result;
       } catch (WxErrorException e) {
+        if (retryTimes + 1 > this.maxRetryTimes) {
+          this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
+          //最后一次重试失败后,直接抛出异常,不再等待
+          throw new RuntimeException("微信服务端异常,超出重试次数");
+        }
+
         WxError error = e.getError();
         /*
          * -1 系统繁忙, 1000ms后重试
@@ -547,8 +555,7 @@ public class WxCpServiceImpl implements WxCpService {
         if (error.getErrorCode() == -1) {
           int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
           try {
-            this.log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis,
-              retryTimes + 1);
+            this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
             Thread.sleep(sleepMillis);
           } catch (InterruptedException e1) {
             throw new RuntimeException(e1);
@@ -557,8 +564,9 @@ public class WxCpServiceImpl implements WxCpService {
           throw e;
         }
       }
-    } while (++retryTimes < this.maxRetryTimes);
+    } while (retryTimes++ < this.maxRetryTimes);
 
+    this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
     throw new RuntimeException("微信服务端异常,超出重试次数");
   }
 
@@ -572,8 +580,7 @@ public class WxCpServiceImpl implements WxCpService {
     uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
 
     try {
-      return executor.execute(getHttpclient(), this.httpProxy,
-        uriWithAccessToken, data);
+      return executor.execute(getHttpclient(), this.httpProxy, uriWithAccessToken, data);
     } catch (WxErrorException e) {
       WxError error = e.getError();
       /*
@@ -586,11 +593,14 @@ public class WxCpServiceImpl implements WxCpService {
         this.configStorage.expireAccessToken();
         return execute(executor, uri, data);
       }
+
       if (error.getErrorCode() != 0) {
+        this.log.error("\n[URL]:  {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data, error);
         throw new WxErrorException(error);
       }
       return null;
     } catch (IOException e) {
+      this.log.error("\n[URL]:  {}\n[PARAMS]: {}\n[EXCEPTION]: {}", uri, data, e.getMessage());
       throw new RuntimeException(e);
     }
   }

+ 7 - 7
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBusyRetryTest.java

@@ -1,17 +1,16 @@
 package me.chanjar.weixin.cp.api;
 
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import me.chanjar.weixin.common.bean.result.WxError;
-import me.chanjar.weixin.common.exception.WxErrorException;
-import me.chanjar.weixin.common.util.http.RequestExecutor;
-
 @Test
 public class WxCpBusyRetryTest {
 
@@ -23,6 +22,7 @@ public class WxCpBusyRetryTest {
       protected synchronized <T, E> T executeInternal(
           RequestExecutor<T, E> executor, String uri, E data)
           throws WxErrorException {
+        this.log.info("Executed");
         WxError error = new WxError();
         error.setErrorCode(-1);
         throw new WxErrorException(error);

+ 80 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java

@@ -0,0 +1,80 @@
+package me.chanjar.weixin.mp.api;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.bean.device.*;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public interface WxMpDeviceService {
+  /**
+   * <pre>
+   * 主动发送消息给设备
+   * 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-3
+   * </pre>
+   */
+  TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException;
+
+  /**
+   * <pre>
+   *   获取一组新的deviceid和设备二维码
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-6
+   * </pre>
+   * @param productId 产品id
+   * @return 返回WxDeviceQrCodeResult
+   */
+  WxDeviceQrCodeResult getQrCode(String productId) throws WxErrorException;
+
+  /**
+   * <pre>
+   *   将device id及其属性信息提交公众平台进行授权
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-6
+   * </pre>
+   * @param wxDeviceAuthorize 授权请求对象
+   * @return WxDeviceAuthorizeResult
+   */
+  WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException;
+
+
+  /**
+   * <pre>
+   *   第三方后台绑定成功后,通知公众平台
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html/page=3-4-7
+   * </pre>
+   * @param wxDeviceBind 绑定请求对象
+   * @return WxDeviceBindResult
+   */
+  WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException;
+
+  /**
+   * <pre>
+   *   强制绑定用户和设备
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-7
+   * </pre>
+   * @param wxDeviceBind 强制绑定请求对象
+   * @return WxDeviceBindResult
+   */
+  WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException;
+
+  /**
+   * <pre>
+   *   第三方确认用户和设备的解绑操作
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html/page=3-4-7
+   * </pre>
+   * @param wxDeviceBind 绑定请求对象
+   * @return WxDeviceBidResult
+   */
+  WxDeviceBindResult unbind(WxDeviceBind wxDeviceBind) throws WxErrorException;
+
+  /**
+   * <pre>
+   *   强制解绑用户和设备
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-7
+   * </pre>
+   * @param wxDeviceBind 强制解绑请求对象
+   * @return WxDeviceBindResult
+   */
+  WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException;
+
+
+}

+ 7 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java

@@ -345,4 +345,11 @@ public interface WxMpService {
    * @return WxMpTemplateMsgService
    */
   WxMpTemplateMsgService getTemplateMsgService();
+
+  /**
+   * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口
+   *
+   * @return WxMpDeviceService
+   */
+  WxMpDeviceService getDeviceService();
 }

+ 72 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java

@@ -0,0 +1,72 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpDeviceService;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.device.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxMpDeviceServiceImpl implements WxMpDeviceService {
+  private static final String API_URL_PREFIX = "https://api.weixin.qq.com/device";
+  private static Logger log = LoggerFactory.getLogger(WxMpMenuServiceImpl.class);
+
+  private WxMpService wxMpService;
+
+  WxMpDeviceServiceImpl(WxMpService wxMpService) {
+    this.wxMpService = wxMpService;
+  }
+
+  @Override
+  public TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException {
+    String url = API_URL_PREFIX + "/transmsg";
+    String response = this.wxMpService.post(url,msg.toJson());
+    return TransMsgResp.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceQrCodeResult getQrCode(String productId) throws WxErrorException {
+    String url = API_URL_PREFIX + "/getqrcode";
+    String response = this.wxMpService.get(url, "product_id=" + productId);
+    return WxDeviceQrCodeResult.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException {
+    String url = API_URL_PREFIX + "/authorize_device";
+    String response = this.wxMpService.post(url,wxDeviceAuthorize.toJson());
+    return WxDeviceAuthorizeResult.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException {
+    String url = API_URL_PREFIX + "/bind";
+    String response = this.wxMpService.post(url,wxDeviceBind.toJson());
+    return WxDeviceBindResult.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException {
+    String url = API_URL_PREFIX + "/compel_bind";
+    String response = this.wxMpService.post(url,wxDeviceBind.toJson());
+    return WxDeviceBindResult.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceBindResult unbind(WxDeviceBind wxDeviceBind) throws WxErrorException {
+    String url = API_URL_PREFIX + "/unbind?";
+    String response = this.wxMpService.post(url, wxDeviceBind.toJson());
+    return WxDeviceBindResult.fromJson(response);
+  }
+
+  @Override
+  public WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException {
+    String url = API_URL_PREFIX + "/compel_unbind?";
+    String response = this.wxMpService.post(url, wxDeviceBind.toJson());
+    return WxDeviceBindResult.fromJson(response);
+  }
+}
+

+ 15 - 2
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java

@@ -62,6 +62,8 @@ public class WxMpServiceImpl implements WxMpService {
 
   private WxMpTemplateMsgService templateMsgService = new WxMpTemplateMsgServiceImpl(this);
 
+  private WxMpDeviceService deviceService = new WxMpDeviceServiceImpl(this);
+
   private CloseableHttpClient httpClient;
 
   private HttpHost httpProxy;
@@ -367,12 +369,18 @@ public class WxMpServiceImpl implements WxMpService {
         this.log.debug("\n[URL]:  {}\n[PARAMS]: {}\n[RESPONSE]: {}",uri, data, result);
         return result;
       } catch (WxErrorException e) {
+        if (retryTimes + 1 > this.maxRetryTimes) {
+          this.log.warn("重试达到最大次数【{}】", maxRetryTimes);
+          //最后一次重试失败后,直接抛出异常,不再等待
+          throw new RuntimeException("微信服务端异常,超出重试次数");
+        }
+
         WxError error = e.getError();
         // -1 系统繁忙, 1000ms后重试
         if (error.getErrorCode() == -1) {
           int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
           try {
-            this.log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
+            this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
             Thread.sleep(sleepMillis);
           } catch (InterruptedException e1) {
             throw new RuntimeException(e1);
@@ -381,8 +389,9 @@ public class WxMpServiceImpl implements WxMpService {
           throw e;
         }
       }
-    } while (++retryTimes < this.maxRetryTimes);
+    } while (retryTimes++ < this.maxRetryTimes);
 
+    this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
     throw new RuntimeException("微信服务端异常,超出重试次数");
   }
 
@@ -540,4 +549,8 @@ public class WxMpServiceImpl implements WxMpService {
     return this.templateMsgService;
   }
 
+  @Override
+  public WxMpDeviceService getDeviceService() {
+    return this.deviceService;
+  }
 }

+ 12 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/AbstractDeviceBean.java

@@ -0,0 +1,12 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+
+/**
+ * Created by keungtung on 14/12/2016.
+ */
+public abstract class AbstractDeviceBean {
+  public String toJson() {
+    return WxGsonBuilder.create().toJson(this);
+  }
+}

+ 60 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java

@@ -0,0 +1,60 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class BaseResp extends AbstractDeviceBean{
+  @SerializedName("base_info")
+  private BaseInfo baseInfo;
+  @SerializedName("errcode")
+  private Integer errCode;
+  @SerializedName("errmsg")
+  private String errMsg;
+
+  public Integer getErrCode() {
+    return errCode;
+  }
+
+  public void setErrCode(Integer errCode) {
+    this.errCode = errCode;
+  }
+
+  public BaseInfo getBaseInfo() {
+    return baseInfo;
+  }
+
+  public void setBaseInfo(BaseInfo baseInfo) {
+    this.baseInfo = baseInfo;
+  }
+
+  public String getErrMsg() {
+    return errMsg;
+  }
+
+  public void setErrMsg(String errMsg) {
+    this.errMsg = errMsg;
+  }
+
+  private class BaseInfo {
+    private String device_type;
+    private String device_id;
+
+    public String getDevice_type() {
+      return device_type;
+    }
+
+    public void setDevice_type(String device_type) {
+      this.device_type = device_type;
+    }
+
+    public String getDevice_id() {
+      return device_id;
+    }
+
+    public void setDevice_id(String device_id) {
+      this.device_id = device_id;
+    }
+  }
+}

+ 30 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/RespMsg.java

@@ -0,0 +1,30 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+
+public class RespMsg extends AbstractDeviceBean{
+  @SerializedName("ret_code")
+  private Integer retCode;
+  @SerializedName("error_info")
+  private String errorInfo;
+
+  public Integer getRetCode() {
+    return retCode;
+  }
+
+  public void setRetCode(Integer retCode) {
+    this.retCode = retCode;
+  }
+
+  public String getErrorInfo() {
+    return errorInfo;
+  }
+
+  public void setErrorInfo(String errorInfo) {
+    this.errorInfo = errorInfo;
+  }
+}

+ 53 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/TransMsgResp.java

@@ -0,0 +1,53 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+
+/**
+ * Created by keungtung on 14/12/2016.
+ */
+public class TransMsgResp extends AbstractDeviceBean{
+  private Integer ret;
+  @SerializedName("ret_info")
+  private String retInfo;
+  @SerializedName("errcode")
+  private Integer errCode;
+  @SerializedName("errmsg")
+  private String errMsg;
+
+  public static TransMsgResp fromJson(String json) {
+    return WxGsonBuilder.create().fromJson(json, TransMsgResp.class);
+  }
+
+  public Integer getRet() {
+    return ret;
+  }
+
+  public void setRet(Integer ret) {
+    this.ret = ret;
+  }
+
+  public String getRetInfo() {
+    return retInfo;
+  }
+
+  public void setRetInfo(String retInfo) {
+    this.retInfo = retInfo;
+  }
+
+  public Integer getErrCode() {
+    return errCode;
+  }
+
+  public void setErrCode(Integer errCode) {
+    this.errCode = errCode;
+  }
+
+  public String getErrMsg() {
+    return errMsg;
+  }
+
+  public void setErrMsg(String errMsg) {
+    this.errMsg = errMsg;
+  }
+}

+ 117 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDevice.java

@@ -0,0 +1,117 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDevice {
+  private String id;
+  private String mac;
+  @SerializedName("connect_protocol")
+  private String connectProtocol;
+  @SerializedName("auth_key")
+  private String authKey;
+  @SerializedName("close_strategy")
+  private String closeStrategy;
+  @SerializedName("conn_strategy")
+  private String connStrategy;
+  @SerializedName("crypt_method")
+  private String cryptMethod;
+  @SerializedName("auth_ver")
+  private String authVer;
+  @SerializedName("manu_mac_pos")
+  private String manuMacPos;
+  @SerializedName("ser_mac_pos")
+  private String serMacPos;
+  @SerializedName("ble_simple_protocol")
+  private String bleSimpleProtocol;
+
+  public String getId() {
+    return id;
+  }
+
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  public String getMac() {
+    return mac;
+  }
+
+  public void setMac(String mac) {
+    this.mac = mac;
+  }
+
+  public String getConnectProtocol() {
+    return connectProtocol;
+  }
+
+  public void setConnectProtocol(String connectProtocol) {
+    this.connectProtocol = connectProtocol;
+  }
+
+  public String getAuthKey() {
+    return authKey;
+  }
+
+  public void setAuthKey(String authKey) {
+    this.authKey = authKey;
+  }
+
+  public String getCloseStrategy() {
+    return closeStrategy;
+  }
+
+  public void setCloseStrategy(String closeStrategy) {
+    this.closeStrategy = closeStrategy;
+  }
+
+  public String getConnStrategy() {
+    return connStrategy;
+  }
+
+  public void setConnStrategy(String connStrategy) {
+    this.connStrategy = connStrategy;
+  }
+
+  public String getCryptMethod() {
+    return cryptMethod;
+  }
+
+  public void setCryptMethod(String cryptMethod) {
+    this.cryptMethod = cryptMethod;
+  }
+
+  public String getAuthVer() {
+    return authVer;
+  }
+
+  public void setAuthVer(String authVer) {
+    this.authVer = authVer;
+  }
+
+  public String getManuMacPos() {
+    return manuMacPos;
+  }
+
+  public void setManuMacPos(String manuMacPos) {
+    this.manuMacPos = manuMacPos;
+  }
+
+  public String getSerMacPos() {
+    return serMacPos;
+  }
+
+  public void setSerMacPos(String serMacPos) {
+    this.serMacPos = serMacPos;
+  }
+
+  public String getBleSimpleProtocol() {
+    return bleSimpleProtocol;
+  }
+
+  public void setBleSimpleProtocol(String bleSimpleProtocol) {
+    this.bleSimpleProtocol = bleSimpleProtocol;
+  }
+}

+ 47 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorize.java

@@ -0,0 +1,47 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceAuthorize extends AbstractDeviceBean {
+  @SerializedName("device_num")
+  private String deviceNum;
+  @SerializedName("op_type")
+  private String opType;
+  @SerializedName("device_list")
+  private List<WxDevice> deviceList = new LinkedList<>();
+
+  public String getDeviceNum() {
+    return deviceNum;
+  }
+
+  public void setDeviceNum(String deviceNum) {
+    this.deviceNum = deviceNum;
+  }
+
+  public String getOpType() {
+    return opType;
+  }
+
+  public void setOpType(String opType) {
+    this.opType = opType;
+  }
+
+  public List<WxDevice> getDeviceList() {
+    return deviceList;
+  }
+
+  public void setDeviceList(List<WxDevice> deviceList) {
+    this.deviceList = deviceList;
+  }
+
+  public void addDevice(WxDevice... devices) {
+    this.deviceList.addAll(Arrays.asList(devices));
+  }
+}

+ 24 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceAuthorizeResult.java

@@ -0,0 +1,24 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+
+import java.util.List;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceAuthorizeResult extends AbstractDeviceBean{
+  private List<BaseResp> resp;
+
+  public static WxDeviceAuthorizeResult fromJson(String response) {
+    return WxGsonBuilder.create().fromJson(response, WxDeviceAuthorizeResult.class);
+  }
+
+  public List<BaseResp> getResp() {
+    return resp;
+  }
+
+  public void setResp(List<BaseResp> resp) {
+    this.resp = resp;
+  }
+}

+ 38 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBind.java

@@ -0,0 +1,38 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceBind extends AbstractDeviceBean{
+  private String ticket;
+  @SerializedName("device_id")
+  private String deviceId;
+  @SerializedName("openid")
+  private String openId;
+
+  public String getTicket() {
+    return ticket;
+  }
+
+  public void setTicket(String ticket) {
+    this.ticket = ticket;
+  }
+
+  public String getDeviceId() {
+    return deviceId;
+  }
+
+  public void setDeviceId(String deviceId) {
+    this.deviceId = deviceId;
+  }
+
+  public String getOpenId() {
+    return openId;
+  }
+
+  public void setOpenId(String openId) {
+    this.openId = openId;
+  }
+}

+ 24 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindResult.java

@@ -0,0 +1,24 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceBindResult extends AbstractDeviceBean{
+  @SerializedName("base_resp")
+  private BaseResp baseResp;
+
+  public static WxDeviceBindResult fromJson(String json) {
+    return WxMpGsonBuilder.create().fromJson(json, WxDeviceBindResult.class);
+  }
+
+  public BaseResp getBaseResp() {
+    return baseResp;
+  }
+
+  public void setBaseResp(BaseResp baseResp) {
+    this.baseResp = baseResp;
+  }
+}

+ 54 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceMsg.java

@@ -0,0 +1,54 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+import me.chanjar.weixin.common.util.ToStringUtils;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceMsg extends AbstractDeviceBean{
+  @SerializedName("device_type")
+  private String deviceType;
+  @SerializedName("device_id")
+  private String deviceId;
+  @SerializedName("open_id")
+  private String openId;
+  private String content;
+
+  @Override
+  public String toString() {
+    return ToStringUtils.toSimpleString(this);
+  }
+
+  public String getDeviceType() {
+    return deviceType;
+  }
+
+  public void setDeviceType(String deviceType) {
+    this.deviceType = deviceType;
+  }
+
+  public String getDeviceId() {
+    return deviceId;
+  }
+
+  public void setDeviceId(String deviceId) {
+    this.deviceId = deviceId;
+  }
+
+  public String getOpenId() {
+    return openId;
+  }
+
+  public void setOpenId(String openId) {
+    this.openId = openId;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  public void setContent(String content) {
+    this.content = content;
+  }
+}

+ 54 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java

@@ -0,0 +1,54 @@
+package me.chanjar.weixin.mp.bean.device;
+
+import com.google.gson.annotations.SerializedName;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+
+/**
+ * Created by keungtung on 10/12/2016.
+ */
+public class WxDeviceQrCodeResult extends AbstractDeviceBean{
+  @SerializedName("deviceid")
+  private String deviceId;
+  @SerializedName("qrticket")
+  private String qrTicket;
+  @SerializedName("devicelicence")
+  private String deviceLicence;
+  @SerializedName("resp_msg")
+  private RespMsg respMsg;
+
+  public static WxDeviceQrCodeResult fromJson(String json) {
+    return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxDeviceQrCodeResult.class);
+  }
+
+  public String getDeviceLicence() {
+    return deviceLicence;
+  }
+
+  public void setDeviceLicence(String deviceLicence) {
+    this.deviceLicence = deviceLicence;
+  }
+
+  public RespMsg getRespMsg() {
+    return respMsg;
+  }
+
+  public void setRespMsg(RespMsg respMsg) {
+    this.respMsg = respMsg;
+  }
+
+  public String getDeviceId() {
+    return deviceId;
+  }
+
+  public void setDeviceId(String deviceId) {
+    this.deviceId = deviceId;
+  }
+
+  public String getQrTicket() {
+    return qrTicket;
+  }
+
+  public void setQrTicket(String qrTicket) {
+    this.qrTicket = qrTicket;
+  }
+}

+ 9 - 11
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBusyRetryTest.java

@@ -1,17 +1,16 @@
 package me.chanjar.weixin.mp.api;
 
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
 import me.chanjar.weixin.common.bean.result.WxError;
 import me.chanjar.weixin.common.exception.WxErrorException;
 import me.chanjar.weixin.common.util.http.RequestExecutor;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 @Test
 public class WxMpBusyRetryTest {
@@ -24,6 +23,7 @@ public class WxMpBusyRetryTest {
       protected synchronized <T, E> T executeInternal(
           RequestExecutor<T, E> executor, String uri, E data)
           throws WxErrorException {
+        this.log.info("Executed");
         WxError error = new WxError();
         error.setErrorCode(-1);
         throw new WxErrorException(error);
@@ -32,9 +32,7 @@ public class WxMpBusyRetryTest {
 
     service.setMaxRetryTimes(3);
     service.setRetrySleepMillis(500);
-    return new Object[][] {
-        new Object[] { service }
-    };
+    return new Object[][] { { service } };
   }
 
   @Test(dataProvider = "getService", expectedExceptions = RuntimeException.class)

+ 40 - 0
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImplTest.java

@@ -0,0 +1,40 @@
+package me.chanjar.weixin.mp.api.impl;
+
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.mp.api.ApiTestModule;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.device.WxDeviceQrCodeResult;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+/**
+ * Created by keungtung on 14/12/2016.
+ */
+@Test(groups = "deviceApi")
+@Guice(modules = ApiTestModule.class)
+public class WxMpDeviceServiceImplTest {
+  @Inject
+  protected WxMpService wxService;
+
+  @Test(dataProvider = "productId")
+  public void testGetQrcode(String productId) {
+    try {
+      WxDeviceQrCodeResult result = wxService.getDeviceService().getQrCode(productId);
+      println(result.toJson());
+    } catch (WxErrorException e) {
+      println(e.getMessage());
+    }
+  }
+
+  private void println(String content) {
+    System.out.println(content);
+  }
+
+  @DataProvider(name = "productId")
+  public Object[][] getProductId() {
+    return new Object[][]{new Object[]{"25639"}};
+  }
+}

+ 104 - 104
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java

@@ -10,63 +10,63 @@ public class WxMpXmlMessageTest {
   public void testFromXml() {
 
     String xml = "<xml>"
-                + "<ToUserName><![CDATA[toUser]]></ToUserName>"
-                + "<FromUserName><![CDATA[fromUser]]></FromUserName> "
-                + "<CreateTime>1348831860</CreateTime>"
-                + "<MsgType><![CDATA[text]]></MsgType>"
-                + "<Content><![CDATA[this is a test]]></Content>"
-                + "<MsgId>1234567890123456</MsgId>"
-                + "<PicUrl><![CDATA[this is a url]]></PicUrl>"
-                + "<MediaId><![CDATA[media_id]]></MediaId>"
-                + "<Format><![CDATA[Format]]></Format>"
-                + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
-                + "<Location_X>23.134521</Location_X>"
-                + "<Location_Y>113.358803</Location_Y>"
-                + "<Scale>20</Scale>"
-                + "<Label><![CDATA[位置信息]]></Label>"
-                + "<Description><![CDATA[公众平台官网链接]]></Description>"
-                + "<Url><![CDATA[url]]></Url>"
-                + "<Title><![CDATA[公众平台官网链接]]></Title>"
-                + "<Event><![CDATA[subscribe]]></Event>"
-                + "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
-                + "<Ticket><![CDATA[TICKET]]></Ticket>"
-                + "<Latitude>23.137466</Latitude>"
-                + "<Longitude>113.352425</Longitude>"
-                + "<Precision>119.385040</Precision>"
-                + "<ScanCodeInfo>"
-                + " <ScanType><![CDATA[qrcode]]></ScanType>"
-                + " <ScanResult><![CDATA[1]]></ScanResult>"
-                + "</ScanCodeInfo>"
-                + "<SendPicsInfo>"
-                + " <Count>1</Count>\n"
-                + " <PicList>"
-                + "  <item>"
-                + "   <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
-                + "  </item>"
-                + " </PicList>"
-                + "</SendPicsInfo>"
-                + "<SendLocationInfo>"
-                + "  <Location_X><![CDATA[23]]></Location_X>\n"
-                + "  <Location_Y><![CDATA[113]]></Location_Y>\n"
-                + "  <Scale><![CDATA[15]]></Scale>\n"
-                + "  <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
-                + "  <Poiname><![CDATA[wo de poi]]></Poiname>\n"
-                + "</SendLocationInfo>"
-                + "</xml>";
+      + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+      + "<FromUserName><![CDATA[fromUser]]></FromUserName> "
+      + "<CreateTime>1348831860</CreateTime>"
+      + "<MsgType><![CDATA[text]]></MsgType>"
+      + "<Content><![CDATA[this is a test]]></Content>"
+      + "<MsgId>1234567890123456</MsgId>"
+      + "<PicUrl><![CDATA[this is a url]]></PicUrl>"
+      + "<MediaId><![CDATA[media_id]]></MediaId>"
+      + "<Format><![CDATA[Format]]></Format>"
+      + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
+      + "<Location_X>23.134521</Location_X>"
+      + "<Location_Y>113.358803</Location_Y>"
+      + "<Scale>20</Scale>"
+      + "<Label><![CDATA[位置信息]]></Label>"
+      + "<Description><![CDATA[公众平台官网链接]]></Description>"
+      + "<Url><![CDATA[url]]></Url>"
+      + "<Title><![CDATA[公众平台官网链接]]></Title>"
+      + "<Event><![CDATA[subscribe]]></Event>"
+      + "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
+      + "<Ticket><![CDATA[TICKET]]></Ticket>"
+      + "<Latitude>23.137466</Latitude>"
+      + "<Longitude>113.352425</Longitude>"
+      + "<Precision>119.385040</Precision>"
+      + "<ScanCodeInfo>"
+      + " <ScanType><![CDATA[qrcode]]></ScanType>"
+      + " <ScanResult><![CDATA[1]]></ScanResult>"
+      + "</ScanCodeInfo>"
+      + "<SendPicsInfo>"
+      + " <Count>1</Count>\n"
+      + " <PicList>"
+      + "  <item>"
+      + "   <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
+      + "  </item>"
+      + " </PicList>"
+      + "</SendPicsInfo>"
+      + "<SendLocationInfo>"
+      + "  <Location_X><![CDATA[23]]></Location_X>\n"
+      + "  <Location_Y><![CDATA[113]]></Location_Y>\n"
+      + "  <Scale><![CDATA[15]]></Scale>\n"
+      + "  <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
+      + "  <Poiname><![CDATA[wo de poi]]></Poiname>\n"
+      + "</SendLocationInfo>"
+      + "</xml>";
     WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml);
     Assert.assertEquals(wxMessage.getToUser(), "toUser");
     Assert.assertEquals(wxMessage.getFromUser(), "fromUser");
-    Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l));
+    Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L));
     Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT);
     Assert.assertEquals(wxMessage.getContent(), "this is a test");
-    Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l));
+    Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L));
     Assert.assertEquals(wxMessage.getPicUrl(), "this is a url");
     Assert.assertEquals(wxMessage.getMediaId(), "media_id");
     Assert.assertEquals(wxMessage.getFormat(), "Format");
     Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id");
-    Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d));
-    Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d));
-    Assert.assertEquals(wxMessage.getScale(), new Double(20));
+    Assert.assertEquals(wxMessage.getLocationX(), 23.134521d);
+    Assert.assertEquals(wxMessage.getLocationY(), 113.358803d);
+    Assert.assertEquals(wxMessage.getScale(), 20d);
     Assert.assertEquals(wxMessage.getLabel(), "位置信息");
     Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接");
     Assert.assertEquals(wxMessage.getUrl(), "url");
@@ -74,12 +74,12 @@ public class WxMpXmlMessageTest {
     Assert.assertEquals(wxMessage.getEvent(), "subscribe");
     Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123");
     Assert.assertEquals(wxMessage.getTicket(), "TICKET");
-    Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466));
-    Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425));
-    Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040));
+    Assert.assertEquals(wxMessage.getLatitude(), 23.137466);
+    Assert.assertEquals(wxMessage.getLongitude(), 113.352425);
+    Assert.assertEquals(wxMessage.getPrecision(), 119.385040);
     Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode");
     Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1");
-    Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l));
+    Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L));
     Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185");
     Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23");
     Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113");
@@ -91,63 +91,63 @@ public class WxMpXmlMessageTest {
   public void testFromXml2() {
 
     String xml = "<xml>"
-        + "<ToUserName><![CDATA[toUser]]></ToUserName>"
-        + "<FromUserName><![CDATA[fromUser]]></FromUserName> "
-        + "<CreateTime>1348831860</CreateTime>"
-        + "<MsgType><![CDATA[text]]></MsgType>"
-        + "<Content><![CDATA[this is a test]]></Content>"
-        + "<MsgID>1234567890123456</MsgID>"
-        + "<PicUrl><![CDATA[this is a url]]></PicUrl>"
-        + "<MediaId><![CDATA[media_id]]></MediaId>"
-        + "<Format><![CDATA[Format]]></Format>"
-        + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
-        + "<Location_X>23.134521</Location_X>"
-        + "<Location_Y>113.358803</Location_Y>"
-        + "<Scale>20</Scale>"
-        + "<Label><![CDATA[位置信息]]></Label>"
-        + "<Description><![CDATA[公众平台官网链接]]></Description>"
-        + "<Url><![CDATA[url]]></Url>"
-        + "<Title><![CDATA[公众平台官网链接]]></Title>"
-        + "<Event><![CDATA[subscribe]]></Event>"
-        + "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
-        + "<Ticket><![CDATA[TICKET]]></Ticket>"
-        + "<Latitude>23.137466</Latitude>"
-        + "<Longitude>113.352425</Longitude>"
-        + "<Precision>119.385040</Precision>"
-        + "<ScanCodeInfo>"
-        + " <ScanType><![CDATA[qrcode]]></ScanType>"
-        + " <ScanResult><![CDATA[1]]></ScanResult>"
-        + "</ScanCodeInfo>"
-        + "<SendPicsInfo>"
-        + " <Count>1</Count>\n"
-        + " <PicList>"
-        + "  <item>"
-        + "   <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
-        + "  </item>"
-        + " </PicList>"
-        + "</SendPicsInfo>"
-        + "<SendLocationInfo>"
-        + "  <Location_X><![CDATA[23]]></Location_X>\n"
-        + "  <Location_Y><![CDATA[113]]></Location_Y>\n"
-        + "  <Scale><![CDATA[15]]></Scale>\n"
-        + "  <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
-        + "  <Poiname><![CDATA[wo de poi]]></Poiname>\n"
-        + "</SendLocationInfo>"
-        + "</xml>";
+      + "<ToUserName><![CDATA[toUser]]></ToUserName>"
+      + "<FromUserName><![CDATA[fromUser]]></FromUserName> "
+      + "<CreateTime>1348831860</CreateTime>"
+      + "<MsgType><![CDATA[text]]></MsgType>"
+      + "<Content><![CDATA[this is a test]]></Content>"
+      + "<MsgID>1234567890123456</MsgID>"
+      + "<PicUrl><![CDATA[this is a url]]></PicUrl>"
+      + "<MediaId><![CDATA[media_id]]></MediaId>"
+      + "<Format><![CDATA[Format]]></Format>"
+      + "<ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>"
+      + "<Location_X>23.134521</Location_X>"
+      + "<Location_Y>113.358803</Location_Y>"
+      + "<Scale>20</Scale>"
+      + "<Label><![CDATA[位置信息]]></Label>"
+      + "<Description><![CDATA[公众平台官网链接]]></Description>"
+      + "<Url><![CDATA[url]]></Url>"
+      + "<Title><![CDATA[公众平台官网链接]]></Title>"
+      + "<Event><![CDATA[subscribe]]></Event>"
+      + "<EventKey><![CDATA[qrscene_123123]]></EventKey>"
+      + "<Ticket><![CDATA[TICKET]]></Ticket>"
+      + "<Latitude>23.137466</Latitude>"
+      + "<Longitude>113.352425</Longitude>"
+      + "<Precision>119.385040</Precision>"
+      + "<ScanCodeInfo>"
+      + " <ScanType><![CDATA[qrcode]]></ScanType>"
+      + " <ScanResult><![CDATA[1]]></ScanResult>"
+      + "</ScanCodeInfo>"
+      + "<SendPicsInfo>"
+      + " <Count>1</Count>\n"
+      + " <PicList>"
+      + "  <item>"
+      + "   <PicMd5Sum><![CDATA[1b5f7c23b5bf75682a53e7b6d163e185]]></PicMd5Sum>"
+      + "  </item>"
+      + " </PicList>"
+      + "</SendPicsInfo>"
+      + "<SendLocationInfo>"
+      + "  <Location_X><![CDATA[23]]></Location_X>\n"
+      + "  <Location_Y><![CDATA[113]]></Location_Y>\n"
+      + "  <Scale><![CDATA[15]]></Scale>\n"
+      + "  <Label><![CDATA[ 广州市海珠区客村艺苑路 106号]]></Label>\n"
+      + "  <Poiname><![CDATA[wo de poi]]></Poiname>\n"
+      + "</SendLocationInfo>"
+      + "</xml>";
     WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml);
     Assert.assertEquals(wxMessage.getToUser(), "toUser");
     Assert.assertEquals(wxMessage.getFromUser(), "fromUser");
-    Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860l));
+    Assert.assertEquals(wxMessage.getCreateTime(), new Long(1348831860L));
     Assert.assertEquals(wxMessage.getMsgType(), WxConsts.XML_MSG_TEXT);
     Assert.assertEquals(wxMessage.getContent(), "this is a test");
-    Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456l));
+    Assert.assertEquals(wxMessage.getMsgId(), new Long(1234567890123456L));
     Assert.assertEquals(wxMessage.getPicUrl(), "this is a url");
     Assert.assertEquals(wxMessage.getMediaId(), "media_id");
     Assert.assertEquals(wxMessage.getFormat(), "Format");
     Assert.assertEquals(wxMessage.getThumbMediaId(), "thumb_media_id");
-    Assert.assertEquals(wxMessage.getLocationX(), new Double(23.134521d));
-    Assert.assertEquals(wxMessage.getLocationY(), new Double(113.358803d));
-    Assert.assertEquals(wxMessage.getScale(), new Double(20));
+    Assert.assertEquals(wxMessage.getLocationX(), 23.134521d);
+    Assert.assertEquals(wxMessage.getLocationY(), 113.358803d);
+    Assert.assertEquals(wxMessage.getScale(), 20d);
     Assert.assertEquals(wxMessage.getLabel(), "位置信息");
     Assert.assertEquals(wxMessage.getDescription(), "公众平台官网链接");
     Assert.assertEquals(wxMessage.getUrl(), "url");
@@ -155,12 +155,12 @@ public class WxMpXmlMessageTest {
     Assert.assertEquals(wxMessage.getEvent(), "subscribe");
     Assert.assertEquals(wxMessage.getEventKey(), "qrscene_123123");
     Assert.assertEquals(wxMessage.getTicket(), "TICKET");
-    Assert.assertEquals(wxMessage.getLatitude(), new Double(23.137466));
-    Assert.assertEquals(wxMessage.getLongitude(), new Double(113.352425));
-    Assert.assertEquals(wxMessage.getPrecision(), new Double(119.385040));
+    Assert.assertEquals(wxMessage.getLatitude(), 23.137466);
+    Assert.assertEquals(wxMessage.getLongitude(), 113.352425);
+    Assert.assertEquals(wxMessage.getPrecision(), 119.385040);
     Assert.assertEquals(wxMessage.getScanCodeInfo().getScanType(), "qrcode");
     Assert.assertEquals(wxMessage.getScanCodeInfo().getScanResult(), "1");
-    Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1l));
+    Assert.assertEquals(wxMessage.getSendPicsInfo().getCount(), new Long(1L));
     Assert.assertEquals(wxMessage.getSendPicsInfo().getPicList().get(0).getPicMd5Sum(), "1b5f7c23b5bf75682a53e7b6d163e185");
     Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationX(), "23");
     Assert.assertEquals(wxMessage.getSendLocationInfo().getLocationY(), "113");