Browse Source

#759 增加三方平台代小程序业务接口:代码管理、模板管理、成员管理、发布审核等

yuanqixun 6 years ago
parent
commit
941833ea1a
27 changed files with 1524 additions and 31 deletions
  1. 4 5
      weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java
  2. 1 0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
  3. 2 2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java
  4. 9 4
      weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
  5. 335 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java
  6. 17 18
      weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
  7. 310 2
      weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java
  8. 96 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java
  9. 21 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenNetworkTimeout.java
  10. 22 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenPage.java
  11. 21 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTab.java
  12. 49 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTabBar.java
  13. 34 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenWindow.java
  14. 86 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaQrcodeParam.java
  15. 40 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaCategory.java
  16. 19 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaMember.java
  17. 60 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaSubmitAudit.java
  18. 21 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java
  19. 31 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaCategoryListResult.java
  20. 33 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaDomainResult.java
  21. 30 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaPageListResult.java
  22. 26 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSubmitAuditResult.java
  23. 31 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaTesterListResult.java
  24. 68 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeApacheHttpRequestExecutor.java
  25. 62 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeJoddHttpRequestExecutor.java
  26. 59 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeOkhttpRequestExecutor.java
  27. 37 0
      weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java

+ 4 - 5
weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java

@@ -4,16 +4,13 @@ import cn.binarywang.wx.miniapp.api.*;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
 import com.google.common.base.Joiner;
+import com.google.gson.Gson;
 import me.chanjar.weixin.common.bean.WxAccessToken;
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.DataUtils;
 import me.chanjar.weixin.common.util.crypto.SHA1;
-import me.chanjar.weixin.common.util.http.HttpType;
-import me.chanjar.weixin.common.util.http.RequestExecutor;
-import me.chanjar.weixin.common.util.http.RequestHttp;
-import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
-import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
+import me.chanjar.weixin.common.util.http.*;
 import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
 import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
 import org.apache.http.HttpHost;
@@ -55,6 +52,8 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl
   private int retrySleepMillis = 1000;
   private int maxRetryTimes = 5;
 
+  protected static final Gson GSON = new Gson();
+
   @Override
   public CloseableHttpClient getRequestHttpClient() {
     return httpClient;

+ 1 - 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java

@@ -237,6 +237,7 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
     jsonObject.addProperty("code", code);
 
     String responseContent = this.getWxMpService().post(MEMBER_CARD_USER_INFO_GET, jsonObject.toString());
+    log.debug("{}",responseContent);
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxMpGsonBuilder.INSTANCE.create().fromJson(tmpJsonElement,
       new TypeToken<WxMpMemberCardUserInfoResult>() {

+ 2 - 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImplTest.java

@@ -22,8 +22,8 @@ public class WxMpMemberCardServiceImplTest {
 
   @Inject
   protected WxMpService wxService;
-  private String cardId = "p2iQk1kUixiypVJ1lJYIT-_fMdUg";
-  private String code = "201808290001";
+  private String cardId = "p2iQk1g2d03JXhVRDY5fZRVr236A";
+  private String code = "435223630779";
   private String openId = "o2iQk1u5X-XIJkatmAK1Q8VVuS90";
 
   @Test

+ 9 - 4
weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java

@@ -1,8 +1,5 @@
 package me.chanjar.weixin.open.api;
 
-import java.util.List;
-
-import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
@@ -13,6 +10,8 @@ import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
 import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
 import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
 
+import java.util.List;
+
 /**
  * @author <a href="https://github.com/007gzs">007</a>
  */
@@ -41,7 +40,13 @@ public interface WxOpenComponentService {
 
   WxMpService getWxMpServiceByAppid(String appid);
 
-  WxMaService getWxMaServiceByAppid(String appid);
+  /**
+   * 获取指定appid的开放平台小程序服务(继承一般小程序服务能力)
+   *
+   * @param appid
+   * @return
+   */
+  WxOpenMaService getWxMaServiceByAppid(String appid);
 
   WxOpenConfigStorage getWxOpenConfigStorage();
 

+ 335 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenMaService.java

@@ -0,0 +1,335 @@
+package me.chanjar.weixin.open.api;
+
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo;
+import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
+import me.chanjar.weixin.open.bean.result.*;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <pre>
+ *     微信开放平台代小程序实现服务能力
+ *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489144594_DhNoV&token=&lang=zh_CN
+ * </pre>
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+public interface WxOpenMaService extends WxMaService {
+
+  /**
+   * 设置小程序服务器域名
+   *
+   * <pre>
+   *     授权给第三方的小程序,其服务器域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的服务器域名将被删除,
+   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加第三方自身的域名。
+   *     提示:需要先将域名登记到第三方平台的小程序服务器域名中,才可以调用接口进行配置
+   * </pre>
+   */
+  String API_MODIFY_DOMAIN = "https://api.weixin.qq.com/wxa/modify_domain";
+
+  /**
+   * 设置小程序业务域名(仅供第三方代小程序调用)
+   * <pre>
+   *     授权给第三方的小程序,其业务域名只可以为第三方的服务器,当小程序通过第三方发布代码上线后,小程序原先自己配置的业务域名将被删除,
+   *     只保留第三方平台的域名,所以第三方平台在代替小程序发布代码之前,需要调用接口为小程序添加业务域名。
+   * 提示:
+   * 1、需要先将域名登记到第三方平台的小程序业务域名中,才可以调用接口进行配置。
+   * 2、为授权的小程序配置域名时支持配置子域名,例如第三方登记的业务域名如为qq.com,则可以直接将qq.com及其子域名(如xxx.qq.com)也配置到授权的小程序中。
+   * </pre>
+   */
+  String API_SET_WEBVIEW_DOMAIN = "https://api.weixin.qq.com/wxa/setwebviewdomain";
+
+  /**
+   * 获取帐号基本信息
+   * <pre>
+   * GET请求
+   * 注意:需要使用1.3环节获取到的新创建小程序appid及authorization_code换取authorizer_refresh_token进而得到authorizer_access_token。
+   * </pre>
+   */
+  String API_GET_ACCOUNT_BASICINFO = "https://api.weixin.qq.com/cgi-bin/account/getaccountbasicinfo";
+
+  /**
+   * 绑定微信用户为小程序体验者
+   */
+  String API_BIND_TESTER = "https://api.weixin.qq.com/wxa/bind_tester";
+
+
+  /**
+   * 解除绑定微信用户为小程序体验者
+   */
+  String API_UNBIND_TESTER = "https://api.weixin.qq.com/wxa/unbind_tester";
+
+
+  /**
+   * 获取体验者列表
+   */
+  String API_GET_TESTERLIST = "https://api.weixin.qq.com/wxa/memberauth";
+
+  /**
+   * 以下接口为三方平台代小程序实现的代码管理功能
+   * <p>
+   *     https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=fe774228c66725425675810097f9e48d0737a4bf&lang=zh_CN
+   * </p>
+   */
+
+  /**
+   * 1. 为授权的小程序帐号上传小程序代码
+   */
+  String API_CODE_COMMIT = "https://api.weixin.qq.com/wxa/commit";
+
+  /**
+   * 2. 获取体验小程序的体验二维码
+   */
+  String API_TEST_QRCODE = "https://api.weixin.qq.com/wxa/get_qrcode";
+
+  /**
+   * 3. 获取授权小程序帐号的可选类目
+   */
+  String API_GET_CATEGORY = "https://api.weixin.qq.com/wxa/get_category";
+
+  /**
+   * 4. 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
+   */
+  String API_GET_PAGE = "https://api.weixin.qq.com/wxa/get_page";
+
+  /**
+   * 5. 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
+   */
+  String API_SUBMIT_AUDIT = "https://api.weixin.qq.com/wxa/submit_audit";
+
+  /**
+   * 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用)
+   */
+  String API_GET_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_auditstatus";
+
+  /**
+   * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用)
+   */
+  String API_GET_LATEST_AUDIT_STATUS = "https://api.weixin.qq.com/wxa/get_latest_auditstatus";
+
+  /**
+   * 9. 发布已通过审核的小程序(仅供第三方代小程序调用)
+   */
+  String API_RELEASE = "https://api.weixin.qq.com/wxa/release";
+
+  /**
+   * 10. 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
+   */
+  String API_CHANGE_VISITSTATUS = "https://api.weixin.qq.com/wxa/change_visitstatus";
+
+  /**
+   * 11.小程序版本回退(仅供第三方代小程序调用)
+   */
+  String API_REVERT_CODE_RELEASE = "https://api.weixin.qq.com/wxa/revertcoderelease";
+
+  /**
+   * 12.查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
+   */
+  String API_GET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion";
+
+  /**
+   * 13.设置最低基础库版本(仅供第三方代小程序调用)
+   */
+  String API_SET_WEAPP_SUPPORT_VERSION = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion";
+
+  /**
+   * 14.设置小程序“扫普通链接二维码打开小程序”能力
+   * <p>
+   *     TODO 暂时不实现
+   * </p>
+   */
+
+  /**
+   * 15.小程序审核撤回
+   * <p>
+   * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
+   * </p>
+   */
+  String API_UNDO_CODE_AUDIT = "https://api.weixin.qq.com/wxa/undocodeaudit";
+
+  /**
+   * 16.1 小程序分阶段发布-分阶段发布接口
+   */
+  String API_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/grayrelease";
+
+  /**
+   * 16.2 小程序分阶段发布-取消分阶段发布
+   */
+  String API_REVERT_GRAY_RELEASE = "https://api.weixin.qq.com/wxa/revertgrayrelease";
+
+  /**
+   * 16.3 小程序分阶段发布-查询当前分阶段发布详情
+   */
+  String API_GET_GRAY_RELEASE_PLAN = "https://api.weixin.qq.com/wxa/getgrayreleaseplan";
+
+
+  /**
+   * 获得小程序的域名配置信息
+   *
+   * @return
+   */
+  WxOpenMaDomainResult getDomain() throws WxErrorException;
+
+  /**
+   * 修改域名
+   *
+   * @param action              delete删除, set覆盖, get获取
+   * @param requestdomainList
+   * @param wsrequestdomainList
+   * @param uploaddomainList
+   * @param downloaddomainList
+   * @return
+   * @throws WxErrorException
+   */
+  WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException;
+
+  /**
+   * 获取小程序的业务域名
+   *
+   * @return 直接返回字符串
+   */
+  String getWebViewDomain() throws WxErrorException;
+
+  /**
+   * 设置小程序的业务域名
+   *
+   * @param action     add添加, delete删除, set覆盖
+   * @param domainList
+   * @return 直接返回字符串
+   */
+  String setWebViewDomain(String action, List<String> domainList) throws WxErrorException;
+
+  /**
+   * 获取小程序的信息
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  String getAccountBasicInfo() throws WxErrorException;
+
+  /**
+   * 绑定小程序体验者
+   *
+   * @param wechatid 体验者微信号(不是openid)
+   * @return
+   * @throws WxErrorException
+   */
+  String bindTester(String wechatid) throws WxErrorException;
+
+  /**
+   * 解除绑定小程序体验者
+   *
+   * @param wechatid 体验者微信号(不是openid)
+   * @return
+   * @throws WxErrorException
+   */
+  String unbindTester(String wechatid) throws WxErrorException;
+
+  /**
+   * 获得体验者列表
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  WxOpenMaTesterListResult getTesterList() throws WxErrorException;
+
+  /**
+   * 1、为授权的小程序帐号上传小程序代码
+   *
+   * @param templateId  代码模板ID
+   * @param userVersion 用户定义版本
+   * @param userDesc    用户定义版本描述
+   * @param extInfo     第三方自定义的配置
+   * @return
+   * @throws WxErrorException
+   */
+  String codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException;
+
+  /**
+   * 获取体验小程序的体验二维码
+   *
+   * @param pagePath
+   * @param params
+   * @return
+   */
+  File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException;
+
+  /**
+   * 获取授权小程序帐号的可选类目
+   * <p>
+   * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。
+   * </p>
+   *
+   * @return WxOpenMaCategoryListResult
+   * @throws WxErrorException
+   */
+  WxOpenMaCategoryListResult getCategoryList() throws WxErrorException;
+
+  /**
+   * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  WxOpenMaPageListResult getPageList() throws WxErrorException;
+
+  /**
+   * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
+   *
+   * @param submitAuditMessage
+   * @return
+   * @throws WxErrorException
+   */
+  WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException;
+
+  /**
+   * 查询某个指定版本的审核状态(仅供第三方代小程序调用)
+   *
+   * @param auditid
+   * @return
+   * @throws WxErrorException
+   */
+  String getAuditStatus(Long auditid) throws WxErrorException;
+
+  /**
+   * 查询最新一次提交的审核状态(仅供第三方代小程序调用)
+   *
+   * @param auditid
+   * @return
+   * @throws WxErrorException
+   */
+  String getLatestAuditStatus(Long auditid) throws WxErrorException;
+
+  /**
+   * 发布已通过审核的小程序(仅供第三方代小程序调用)
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  String releaesAudited() throws WxErrorException;
+
+  /**
+   * 11. 小程序版本回退(仅供第三方代小程序调用)
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  String revertCodeReleaes() throws WxErrorException;
+
+  /**
+   * 15. 小程序审核撤回
+   * <p>
+   * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
+   * </p>
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  String undoCodeAudit() throws WxErrorException;
+  
+}

+ 17 - 18
weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java

@@ -1,14 +1,5 @@
 package me.chanjar.weixin.open.api.impl;
 
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import cn.binarywang.wx.miniapp.api.WxMaService;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
@@ -22,6 +13,7 @@ import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
 import me.chanjar.weixin.open.api.WxOpenComponentService;
 import me.chanjar.weixin.open.api.WxOpenConfigStorage;
+import me.chanjar.weixin.open.api.WxOpenMaService;
 import me.chanjar.weixin.open.api.WxOpenService;
 import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
 import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
@@ -32,13 +24,20 @@ import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
 import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerOptionResult;
 import me.chanjar.weixin.open.bean.result.WxOpenQueryAuthResult;
 import me.chanjar.weixin.open.util.json.WxOpenGsonBuilder;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="https://github.com/007gzs">007</a>
  */
 public class WxOpenComponentServiceImpl implements WxOpenComponentService {
   private static final JsonParser JSON_PARSER = new JsonParser();
-  private static final Map<String, WxMaService> WX_OPEN_MA_SERVICE_MAP = new Hashtable<>();
+  private static final Map<String, WxOpenMaService> WX_OPEN_MA_SERVICE_MAP = new Hashtable<>();
   private static final Map<String, WxMpService> WX_OPEN_MP_SERVICE_MAP = new Hashtable<>();
 
   protected final Logger log = LoggerFactory.getLogger(this.getClass());
@@ -65,18 +64,18 @@ public class WxOpenComponentServiceImpl implements WxOpenComponentService {
   }
 
   @Override
-  public WxMaService getWxMaServiceByAppid(String appId) {
-    WxMaService wxMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
-    if (wxMaService == null) {
+  public WxOpenMaService getWxMaServiceByAppid(String appId) {
+    WxOpenMaService wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
+    if (wxOpenMaService == null) {
       synchronized (WX_OPEN_MA_SERVICE_MAP) {
-        wxMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
-        if (wxMaService == null) {
-          wxMaService = new WxOpenMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId));
-          WX_OPEN_MA_SERVICE_MAP.put(appId, wxMaService);
+        wxOpenMaService = WX_OPEN_MA_SERVICE_MAP.get(appId);
+        if (wxOpenMaService == null) {
+          wxOpenMaService = new WxOpenMaServiceImpl(this, appId, getWxOpenConfigStorage().getWxMaConfig(appId));
+          WX_OPEN_MA_SERVICE_MAP.put(appId, wxOpenMaService);
         }
       }
     }
-    return wxMaService;
+    return wxOpenMaService;
   }
 
   public WxOpenService getWxOpenService() {

+ 310 - 2
weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenMaServiceImpl.java

@@ -1,16 +1,34 @@
 package me.chanjar.weixin.open.api.impl;
 
-import cn.binarywang.wx.miniapp.api.WxMaUserService;
 import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
 import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
 import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.open.api.WxOpenComponentService;
+import me.chanjar.weixin.open.api.WxOpenMaService;
+import me.chanjar.weixin.open.bean.ma.WxMaOpenCommitExtInfo;
+import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
+import me.chanjar.weixin.open.bean.message.WxOpenMaSubmitAuditMessage;
+import me.chanjar.weixin.open.bean.result.*;
+import me.chanjar.weixin.open.util.requestexecuter.ma.MaQrCodeRequestExecutor;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="https://github.com/007gzs">007</a>
+ * <pre>
+ *     增加开放平台代小程序管理服务能力
+ *     说明:这里让这个服务公开便于调用者模拟本地测试服务
+ * </pre>
+ * @author yqx
+ * @date 2018-09-12
  */
-/* package */ class WxOpenMaServiceImpl extends WxMaServiceImpl {
+public class WxOpenMaServiceImpl extends WxMaServiceImpl implements WxOpenMaService {
   private WxOpenComponentService wxOpenComponentService;
   private WxMaConfig wxMaConfig;
   private String appId;
@@ -26,6 +44,7 @@ import me.chanjar.weixin.open.api.WxOpenComponentService;
   public WxMaJscode2SessionResult jsCode2SessionInfo(String jsCode) throws WxErrorException {
     return wxOpenComponentService.miniappJscode2Session(appId, jsCode);
   }
+
   @Override
   public WxMaConfig getWxMaConfig() {
     return wxMaConfig;
@@ -35,4 +54,293 @@ import me.chanjar.weixin.open.api.WxOpenComponentService;
   public String getAccessToken(boolean forceRefresh) throws WxErrorException {
     return wxOpenComponentService.getAuthorizerAccessToken(appId, forceRefresh);
   }
+
+  /**
+   * 获得小程序的域名配置信息
+   *
+   * @return
+   */
+  @Override
+  public WxOpenMaDomainResult getDomain() throws WxErrorException {
+    return modifyDomain("get", null, null, null, null);
+  }
+
+  /**
+   * 修改服务器域名
+   *
+   * @param action              delete删除, set覆盖, get获取
+   * @param requestdomainList
+   * @param wsrequestdomainList
+   * @param uploaddomainList
+   * @param downloaddomainList
+   * @return
+   * @throws WxErrorException
+   */
+  public WxOpenMaDomainResult modifyDomain(String action, List<String> requestdomainList, List<String> wsrequestdomainList, List<String> uploaddomainList, List<String> downloaddomainList) throws WxErrorException {
+
+//    if (!"get".equals(action) && (requestdomainList == null || wsrequestdomainList == null || uploaddomainList == null || downloaddomainList == null)) {
+//      throw new WxErrorException(WxError.builder().errorCode(44004).errorMsg("域名参数不能为空").build());
+//    }
+    JsonObject requestJson = new JsonObject();
+    requestJson.addProperty("action", action);
+    if (!"get".equals(action)) {
+      requestJson.add("requestdomain", toJsonArray(requestdomainList));
+      requestJson.add("wsrequestdomain", toJsonArray(wsrequestdomainList));
+      requestJson.add("uploaddomain", toJsonArray(uploaddomainList));
+      requestJson.add("downloaddomain", toJsonArray(downloaddomainList));
+    }
+    String response = post(API_MODIFY_DOMAIN, GSON.toJson(requestJson));
+    return WxMaGsonBuilder.create().fromJson(response, WxOpenMaDomainResult.class);
+  }
+
+  /**
+   * 获取小程序的业务域名
+   *
+   * @return
+   */
+  @Override
+  public String getWebViewDomain() throws WxErrorException {
+    return setWebViewDomain("get", null);
+  }
+
+  /**
+   * 设置小程序的业务域名
+   *
+   * @param action     add添加, delete删除, set覆盖
+   * @param domainList
+   * @return
+   */
+  @Override
+  public String setWebViewDomain(String action, List<String> domainList) throws WxErrorException {
+    JsonObject requestJson = new JsonObject();
+    requestJson.addProperty("action", action);
+    if (!"get".equals(action)) {
+      requestJson.add("webviewdomain", toJsonArray(domainList));
+    }
+    String response = post(API_SET_WEBVIEW_DOMAIN, GSON.toJson(requestJson));
+    //TODO 转化为对象返回
+    return response;
+  }
+
+  /**
+   * 获取小程序的信息,GET请求
+   * <pre>
+   *     注意:这里不能直接用小程序的access_token
+   *     //TODO 待调整
+   * </pre>
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String getAccountBasicInfo() throws WxErrorException {
+    String response = get(API_GET_ACCOUNT_BASICINFO, "");
+    return response;
+  }
+
+  /**
+   * 绑定小程序体验者
+   *
+   * @param wechatid 体验者微信号(不是openid)
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String bindTester(String wechatid) throws WxErrorException {
+    JsonObject paramJson = new JsonObject();
+    paramJson.addProperty("wechatid", wechatid);
+    String response = post(API_BIND_TESTER, GSON.toJson(paramJson));
+    return response;
+  }
+
+  /**
+   * 解除绑定小程序体验者
+   *
+   * @param wechatid 体验者微信号(不是openid)
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String unbindTester(String wechatid) throws WxErrorException {
+    JsonObject paramJson = new JsonObject();
+    paramJson.addProperty("wechatid", wechatid);
+    String response = post(API_UNBIND_TESTER, GSON.toJson(paramJson));
+    return response;
+  }
+
+  /**
+   * 获得体验者列表
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public WxOpenMaTesterListResult getTesterList() throws WxErrorException {
+    JsonObject paramJson = new JsonObject();
+    paramJson.addProperty("action", "get_experiencer");
+    String response = post(API_GET_TESTERLIST, GSON.toJson(paramJson));
+    return WxMaGsonBuilder.create().fromJson(response, WxOpenMaTesterListResult.class);
+  }
+
+  /**
+   * 1、为授权的小程序帐号上传小程序代码
+   *
+   * @param templateId  代码模板ID
+   * @param userVersion 用户定义版本
+   * @param userDesc    用户定义版本描述
+   * @param extInfo     第三方自定义的配置
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String codeCommit(Long templateId, String userVersion, String userDesc, WxMaOpenCommitExtInfo extInfo) throws WxErrorException {
+    JsonObject params = new JsonObject();
+    params.addProperty("template_id", templateId);
+    params.addProperty("user_version", userVersion);
+    params.addProperty("user_desc", userDesc);
+    //注意:ext_json必须是字符串类型
+    params.addProperty("ext_json", GSON.toJson(extInfo));
+    String response = post(API_CODE_COMMIT, GSON.toJson(params));
+    return response;
+  }
+
+  /**
+   * 获取体验小程序的体验二维码
+   *
+   * @param pagePath
+   * @param params
+   * @return
+   */
+  @Override
+  public File getTestQrcode(String pagePath, Map<String, String> params) throws WxErrorException {
+    WxMaQrcodeParam qrcodeParam = WxMaQrcodeParam.create(pagePath);
+    qrcodeParam.addPageParam(params);
+    return execute(MaQrCodeRequestExecutor.create(getRequestHttp()), API_TEST_QRCODE, qrcodeParam);
+  }
+
+  /**
+   * 获取授权小程序帐号的可选类目
+   * <p>
+   * 注意:该接口可获取已设置的二级类目及用于代码审核的可选三级类目。
+   * </p>
+   *
+   * @return WxOpenMaCategoryListResult
+   * @throws WxErrorException
+   */
+  @Override
+  public WxOpenMaCategoryListResult getCategoryList() throws WxErrorException {
+    String response = get(API_GET_CATEGORY, null);
+    return WxMaGsonBuilder.create().fromJson(response, WxOpenMaCategoryListResult.class);
+  }
+
+  /**
+   * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public WxOpenMaPageListResult getPageList() throws WxErrorException {
+    String response = get(API_GET_PAGE, null);
+    return WxMaGsonBuilder.create().fromJson(response, WxOpenMaPageListResult.class);
+  }
+
+  /**
+   * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
+   *
+   * @param submitAuditMessage
+   * @return
+   * @throws WxErrorException
+   */
+  public WxOpenMaSubmitAuditResult submitAudit(WxOpenMaSubmitAuditMessage submitAuditMessage) throws WxErrorException {
+    String response = post(API_SUBMIT_AUDIT, GSON.toJson(submitAuditMessage));
+    return WxMaGsonBuilder.create().fromJson(response, WxOpenMaSubmitAuditResult.class);
+  }
+
+  /**
+   * 7. 查询某个指定版本的审核状态(仅供第三方代小程序调用)
+   *
+   * @param auditid
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String getAuditStatus(Long auditid) throws WxErrorException {
+    JsonObject params = new JsonObject();
+    params.addProperty("auditid", auditid);
+    String response = post(API_GET_AUDIT_STATUS, GSON.toJson(params));
+    return response;
+  }
+
+  /**
+   * 8. 查询最新一次提交的审核状态(仅供第三方代小程序调用)
+   *
+   * @param auditid
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String getLatestAuditStatus(Long auditid) throws WxErrorException {
+    String response = get(API_GET_LATEST_AUDIT_STATUS, null);
+    return response;
+  }
+
+  /**
+   * 9. 发布已通过审核的小程序(仅供第三方代小程序调用)
+   * <p>
+   * 请填写空的数据包,POST的json数据包为空即可。
+   * </p>
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String releaesAudited() throws WxErrorException {
+    JsonObject params = new JsonObject();
+    String response = post(API_RELEASE, GSON.toJson(params));
+    return response;
+  }
+
+  /**
+   * 11. 小程序版本回退(仅供第三方代小程序调用)
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String revertCodeReleaes() throws WxErrorException {
+    String response = get(API_REVERT_CODE_RELEASE, null);
+    return response;
+  }
+
+  /**
+   * 15. 小程序审核撤回
+   * <p>
+   * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次。
+   * </p>
+   *
+   * @return
+   * @throws WxErrorException
+   */
+  @Override
+  public String undoCodeAudit() throws WxErrorException {
+    String response = get(API_UNDO_CODE_AUDIT, null);
+    return response;
+  }
+
+  /**
+   * 将字符串对象转化为GsonArray对象
+   *
+   * @param strList
+   * @return
+   */
+  private JsonArray toJsonArray(List<String> strList) {
+    JsonArray jsonArray = new JsonArray();
+    if (strList != null && !strList.isEmpty()) {
+      for (String str : strList) {
+        jsonArray.add(str);
+      }
+    }
+    return jsonArray;
+  }
 }

+ 96 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenCommitExtInfo.java

@@ -0,0 +1,96 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 微信小程序三方平台代上传代码提交额外信息对象
+ * <p>
+ * 如果代码中已经有配置,则配置的合并规则为:除了pages和tabBar.list直接覆盖原配置,其他都为插入或同级覆盖。
+ * </p>
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaOpenCommitExtInfo implements Serializable {
+
+  WxMaOpenCommitExtInfo() {
+
+  }
+
+  /**
+   * 授权小程序Appid,可填入商户小程序AppID,以区分不同商户
+   */
+  private String extAppid;
+
+  @SerializedName("ext")
+  private Map<String, Object> extMap;
+
+  @SerializedName("extPages")
+  private Map<String, WxMaOpenPage> extPages;
+
+  /**
+   * 页面路径列表(和app.json结构一致)
+   */
+  @SerializedName("pages")
+  private List<String> pageList;
+
+  @SerializedName("window")
+  private WxMaOpenWindow window;
+
+  @SerializedName("networkTimeout")
+  private WxMaOpenNetworkTimeout networkTimeout;
+
+  @SerializedName("tabBar")
+  private WxMaOpenTabBar tabBar;
+
+  /**
+   * 添加扩展项
+   *
+   * @param key
+   * @param value
+   */
+  public void addExt(String key, String value) {
+    if (extMap == null)
+      extMap = new HashMap<>();
+    if (StringUtils.isNoneBlank(key, value))
+      extMap.put(key, value);
+  }
+
+  /**
+   * 添加扩展页面
+   *
+   * @param pagePath
+   * @param page
+   */
+  public void addExtPage(String pagePath, WxMaOpenPage page) {
+    if (extPages == null)
+      extPages = new HashMap<>();
+    if (StringUtils.isNotBlank(pagePath) && page != null)
+      extPages.put(pagePath, page);
+  }
+
+  /**
+   * 添加页面
+   *
+   * @param pagePath
+   */
+  public void addPage(String pagePath) {
+    if (pageList == null)
+      pageList = new ArrayList<>();
+    if (StringUtils.isNotBlank(pagePath))
+      pageList.add(pagePath);
+  }
+
+  public static WxMaOpenCommitExtInfo INSTANCE() {
+    return new WxMaOpenCommitExtInfo();
+  }
+}

+ 21 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenNetworkTimeout.java

@@ -0,0 +1,21 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaOpenNetworkTimeout implements Serializable {
+
+  private Integer request;
+
+  private Integer connectSocket;
+
+  private Integer uploadFile;
+
+  private Integer downloadFile;
+}

+ 22 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenPage.java

@@ -0,0 +1,22 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaOpenPage implements Serializable {
+  private String navigationBarBackgroundColor;
+  private String navigationBarTextStyle;
+  private String navigationBarTitleText;
+  private String backgroundColor;
+  private String backgroundTextStyle;
+  private Boolean enablePullDownRefresh;
+  private Integer onReachBottomDistance;
+  private Boolean disableScroll;
+
+}

+ 21 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTab.java

@@ -0,0 +1,21 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+import lombok.NonNull;
+
+import java.io.Serializable;
+
+/**
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaOpenTab implements Serializable {
+  @NonNull
+  private String pagePath;
+
+  @NonNull
+  private String text;
+  private String iconPath;
+  private String selectedIconPath;
+}

+ 49 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenTabBar.java

@@ -0,0 +1,49 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * tabBar对象
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+@NoArgsConstructor
+public class WxMaOpenTabBar implements Serializable {
+  @NonNull
+  private String color;
+
+  @NonNull
+  private String selectedColor;
+
+  @NonNull
+  private String backgroundColor;
+
+  private String borderStyle;
+
+  @NonNull
+  @SerializedName("list")
+  private List<WxMaOpenTab> tabList;
+
+  private String position;
+
+  /**
+   * 添加tab
+   *
+   * @param text
+   * @param pagePath
+   */
+  public void addTab(String text, String pagePath) {
+    if (tabList == null)
+      tabList = new ArrayList<>();
+    tabList.add(new WxMaOpenTab(pagePath, text));
+  }
+}

+ 34 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaOpenWindow.java

@@ -0,0 +1,34 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * window对象
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaOpenWindow implements Serializable {
+  private String navigationBarBackgroundColor;
+
+  private String navigationBarTextStyle;
+
+  private String navigationBarTitleText;
+
+  private String navigationStyle;
+
+  private String backgroundColor;
+
+  private String backgroundTextStyle;
+
+  private String backgroundColorTop;
+
+  private String backgroundColorBottom;
+
+  private Boolean enablePullDownRefresh;
+
+  private Integer onReachBottomDistance;
+}

+ 86 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxMaQrcodeParam.java

@@ -0,0 +1,86 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 微信小程序体验二维码参数
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxMaQrcodeParam {
+
+  WxMaQrcodeParam() {
+
+  }
+
+  /**
+   * 页面路径
+   */
+  private String pagePath;
+
+  /**
+   * 页面参数
+   */
+  private Map<String, String> pageParams;
+
+  /**
+   * 添加页面参数
+   *
+   * @param key
+   * @param value
+   */
+  public WxMaQrcodeParam addPageParam(String key, String value) {
+    if (StringUtils.isNoneBlank(key, value)) {
+      if (pageParams == null)
+        pageParams = new HashMap<>();
+      pageParams.put(key, value);
+    }
+    return this;
+  }
+
+  /**
+   * 添加页面参数
+   *
+   * @param params
+   * @return
+   */
+  public WxMaQrcodeParam addPageParam(Map<String, String> params) {
+    if (params != null) {
+      if (pageParams == null)
+        pageParams = new HashMap<>();
+      pageParams.putAll(params);
+    }
+    return this;
+  }
+
+  /**
+   * 组装完整的页面请求路径(带参数)
+   *
+   * @return
+   */
+  public String getRequestPath() {
+    if (StringUtils.isNotBlank(getPagePath()) && getPageParams() != null && !getPageParams().isEmpty()) {
+      Set<String> keys = getPageParams().keySet();
+      StringBuilder sb = new StringBuilder();
+      for (String key : keys) {
+        sb.append("&").append(key).append("=").append(getPageParams().get(key));
+      }
+      return pagePath + "?" + sb.substring(1);
+    } else {
+      return pagePath;
+    }
+  }
+
+  public static WxMaQrcodeParam create(String pagePath) {
+    WxMaQrcodeParam instance = new WxMaQrcodeParam();
+    instance.setPagePath(pagePath);
+    return instance;
+  }
+}

+ 40 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaCategory.java

@@ -0,0 +1,40 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+
+/**
+ * 微信小程序分类目录
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxOpenMaCategory implements Serializable {
+
+  @SerializedName("first_class")
+  private String firstClass;
+
+  @SerializedName("second_class")
+  private String secondClass;
+
+  @SerializedName("third_class")
+  private String thirdClass;
+
+  @SerializedName("first_id")
+  private Integer firstId;
+
+  @SerializedName("second_id")
+  private Integer secondId;
+
+  @SerializedName("third_id")
+  private Integer thirdId;
+
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+  }
+}

+ 19 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaMember.java

@@ -0,0 +1,19 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 微信开放平台小程序成员对象
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaMember implements Serializable {
+  /**
+   * 人员对应的唯一字符串
+   */
+  private String userstr;
+}

+ 60 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/ma/WxOpenMaSubmitAudit.java

@@ -0,0 +1,60 @@
+package me.chanjar.weixin.open.bean.ma;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 三方平台提交小程序代码审核
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxOpenMaSubmitAudit implements Serializable {
+
+
+  /**
+   * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得
+   */
+  @SerializedName("address")
+  private String pagePath;
+
+  /**
+   * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20
+   */
+  @SerializedName("tag")
+  private String tag;
+
+  /**
+   * 类目名称,可通过“获取授权小程序帐号的可选类目”接口获得
+   */
+  @SerializedName("first_class")
+  private String firstClass;
+
+  @SerializedName("second_class")
+  private String secondClass;
+
+  @SerializedName("third_class")
+  private String thirdClass;
+
+  /**
+   * 类目的ID,可通过“获取授权小程序帐号的可选类目”接口获得
+   */
+  @SerializedName("first_id")
+  private Integer firstId;
+
+  @SerializedName("second_id")
+  private Integer secondId;
+
+  @SerializedName("third_id")
+  private Integer thirdId;
+
+  /**
+   * 小程序页面的标题,标题长度不超过32
+   */
+  @SerializedName("title")
+  private String title;
+
+}

+ 21 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenMaSubmitAuditMessage.java

@@ -0,0 +1,21 @@
+package me.chanjar.weixin.open.bean.message;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.open.bean.ma.WxOpenMaSubmitAudit;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信小程序代码包提交审核(仅供第三方开发者代小程序调用)
+ *
+ * @author yqx
+ * @date 2018/9/13
+ */
+@Data
+public class WxOpenMaSubmitAuditMessage implements Serializable {
+
+  @SerializedName("item_list")
+  private List<WxOpenMaSubmitAudit> itemList;
+}

+ 31 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaCategoryListResult.java

@@ -0,0 +1,31 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.open.bean.ma.WxOpenMaCategory;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信开放平台小程序分类目录列表返回
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaCategoryListResult implements Serializable {
+
+  private String errcode;
+  private String errmsg;
+
+  @SerializedName("category_list")
+  List<WxOpenMaCategory> categoryList;
+
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+  }
+
+}

+ 33 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaDomainResult.java

@@ -0,0 +1,33 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信开放平台小程序域名设置返回对象
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaDomainResult implements Serializable {
+
+  private String errcode;
+  private String errmsg;
+
+  @SerializedName("requestdomain")
+  List<String> requestdomainList;
+
+  @SerializedName("wsrequestdomain")
+  List<String> wsrequestdomainList;
+
+  @SerializedName("uploaddomain")
+  List<String> uploaddomainList;
+
+  @SerializedName("downloaddomain")
+  List<String> downloaddomainList;
+  
+}

+ 30 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaPageListResult.java

@@ -0,0 +1,30 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信开放平台小程序第三方提交代码的页面配置列表
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaPageListResult implements Serializable {
+
+  private String errcode;
+  private String errmsg;
+
+  @SerializedName("page_list")
+  List<String> pageList;
+
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+  }
+
+}

+ 26 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaSubmitAuditResult.java

@@ -0,0 +1,26 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 微信开放平台小程序发布代码审核结果
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaSubmitAuditResult implements Serializable {
+
+  private String errcode;
+  private String errmsg;
+
+  /**
+   * 审核编号
+   */
+  @SerializedName("auditid")
+  Long auditId;
+
+}

+ 31 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/result/WxOpenMaTesterListResult.java

@@ -0,0 +1,31 @@
+package me.chanjar.weixin.open.bean.result;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import me.chanjar.weixin.open.bean.ma.WxOpenMaMember;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 微信开放平台小程序体验者列表返回
+ *
+ * @author yqx
+ * @date 2018/9/12
+ */
+@Data
+public class WxOpenMaTesterListResult implements Serializable {
+
+  private String errcode;
+  private String errmsg;
+
+  @SerializedName("members")
+  List<WxOpenMaMember> membersList;
+
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+  }
+
+}

+ 68 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeApacheHttpRequestExecutor.java

@@ -0,0 +1,68 @@
+package me.chanjar.weixin.open.util.requestexecuter.ma;
+
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler;
+import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
+import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.CloseableHttpClient;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.UUID;
+
+/**
+ * @author yqx
+ * @date 2018-09-13
+ */
+public class MaQrCodeApacheHttpRequestExecutor extends MaQrCodeRequestExecutor<CloseableHttpClient, HttpHost> {
+  public MaQrCodeApacheHttpRequestExecutor(RequestHttp requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public File execute(String uri, WxMaQrcodeParam qrcodeParam) throws WxErrorException, IOException {
+    if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) {
+      if (uri.indexOf('?') == -1) {
+        uri += '?';
+      }
+      uri += uri.endsWith("?")
+        ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8")
+        : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8");
+    }
+
+    HttpGet httpGet = new HttpGet(uri);
+    if (requestHttp.getRequestHttpProxy() != null) {
+      RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
+      httpGet.setConfig(config);
+    }
+
+    try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet);
+         InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) {
+      Header[] contentTypeHeader = response.getHeaders("Content-Type");
+      if (contentTypeHeader != null && contentTypeHeader.length > 0) {
+        // 出错
+        if (ContentType.TEXT_PLAIN.getMimeType()
+          .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) {
+          String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
+          throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+        }
+      }
+      return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
+    } finally {
+      httpGet.releaseConnection();
+    }
+  }
+}

+ 62 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeJoddHttpRequestExecutor.java

@@ -0,0 +1,62 @@
+package me.chanjar.weixin.open.util.requestexecuter.ma;
+
+import jodd.http.HttpConnectionProvider;
+import jodd.http.HttpRequest;
+import jodd.http.HttpResponse;
+import jodd.http.ProxyInfo;
+import jodd.util.MimeTypes;
+import jodd.util.StringPool;
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.UUID;
+
+/**
+ * @author yqx
+ * @date 2018-09-13
+ */
+public class MaQrCodeJoddHttpRequestExecutor extends MaQrCodeRequestExecutor<HttpConnectionProvider, ProxyInfo> {
+  public MaQrCodeJoddHttpRequestExecutor(RequestHttp requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public File execute(String uri, WxMaQrcodeParam qrcodeParam) throws WxErrorException, IOException {
+    if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) {
+      if (uri.indexOf('?') == -1) {
+        uri += '?';
+      }
+      uri += uri.endsWith("?")
+        ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8")
+        : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8");
+    }
+
+
+    HttpRequest request = HttpRequest.get(uri);
+    if (requestHttp.getRequestHttpProxy() != null) {
+      requestHttp.getRequestHttpClient().useProxy(requestHttp.getRequestHttpProxy());
+    }
+    request.withConnectionProvider(requestHttp.getRequestHttpClient());
+
+    HttpResponse response = request.send();
+    response.charset(StringPool.UTF_8);
+    String contentTypeHeader = response.header("Content-Type");
+    if (MimeTypes.MIME_TEXT_PLAIN.equals(contentTypeHeader)) {
+      String responseContent = response.bodyText();
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+    }
+    try (InputStream inputStream = new ByteArrayInputStream(response.bodyBytes())) {
+      return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
+    }
+  }
+}

+ 59 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeOkhttpRequestExecutor.java

@@ -0,0 +1,59 @@
+package me.chanjar.weixin.open.util.requestexecuter.ma;
+
+import me.chanjar.weixin.common.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.fs.FileUtils;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
+import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.UUID;
+
+/**
+ * @author yqx
+ * @date 2018-09-13
+ */
+public class MaQrCodeOkhttpRequestExecutor extends MaQrCodeRequestExecutor<OkHttpClient, OkHttpProxyInfo> {
+  private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+  public MaQrCodeOkhttpRequestExecutor(RequestHttp requestHttp) {
+    super(requestHttp);
+  }
+
+  @Override
+  public File execute(String uri, WxMaQrcodeParam qrcodeParam) throws WxErrorException, IOException {
+    if (qrcodeParam != null && StringUtils.isNotBlank(qrcodeParam.getPagePath())) {
+      if (uri.indexOf('?') == -1) {
+        uri += '?';
+      }
+      uri += uri.endsWith("?")
+        ? "path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8")
+        : "&path=" + URLEncoder.encode(qrcodeParam.getRequestPath(), "UTF-8");
+    }
+
+    OkHttpClient client = requestHttp.getRequestHttpClient();
+    Request request = new Request.Builder().url(uri).get().build();
+    Response response = client.newCall(request).execute();
+    String contentTypeHeader = response.header("Content-Type");
+    if ("text/plain".equals(contentTypeHeader)) {
+      String responseContent = response.body().string();
+      throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
+    }
+
+    try (InputStream inputStream = response.body().byteStream()) {
+      return FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), "jpg");
+    }
+
+  }
+}

+ 37 - 0
weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java

@@ -0,0 +1,37 @@
+package me.chanjar.weixin.open.util.requestexecuter.ma;
+
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestHttp;
+import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam;
+
+import java.io.File;
+
+/**
+ * 获得小程序体验QrCode图片 请求执行器
+ *
+ * @author yqx
+ * @date 2018-09-13
+ */
+public abstract class MaQrCodeRequestExecutor<H, P> implements RequestExecutor<File, WxMaQrcodeParam> {
+  protected RequestHttp<H, P> requestHttp;
+
+  public MaQrCodeRequestExecutor(RequestHttp requestHttp) {
+    this.requestHttp = requestHttp;
+  }
+
+  public static RequestExecutor<File, WxMaQrcodeParam> create(RequestHttp requestHttp) throws WxErrorException {
+    switch (requestHttp.getRequestType()) {
+      case APACHE_HTTP:
+        return new MaQrCodeApacheHttpRequestExecutor(requestHttp);
+      case JODD_HTTP:
+        return new MaQrCodeJoddHttpRequestExecutor(requestHttp);
+      case OK_HTTP:
+        return new MaQrCodeOkhttpRequestExecutor(requestHttp);
+      default:
+        throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("不支持的http框架").build());
+    }
+  }
+
+}