diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java
new file mode 100644
index 00000000..37e850ee
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaCodeService.java
@@ -0,0 +1,140 @@
+package cn.binarywang.wx.miniapp.api;
+
+import cn.binarywang.wx.miniapp.bean.code.WxMaCategory;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
+import me.chanjar.weixin.common.exception.WxErrorException;
+
+import java.util.List;
+
+/**
+ * 小程序代码管理相关 API(大部分只能是第三方平台调用)
+ * 文档:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1489140610_Uavc4&token=&lang=zh_CN
+ *
+ * @author Charming
+ * @since 2018-04-26 19:43
+ */
+public interface WxMaCodeService {
+ /**
+ * 为授权的小程序帐号上传小程序代码
+ */
+ String COMMIT_URL = "https://api.weixin.qq.com/wxa/commit";
+ String GET_QRCODE_URL = "https://api.weixin.qq.com/wxa/get_qrcode";
+ String GET_CATEGORY_URL = "https://api.weixin.qq.com/wxa/get_category";
+ String GET_PAGE_URL = "https://api.weixin.qq.com/wxa/get_page";
+ String SUBMIT_AUDIT_URL = "https://api.weixin.qq.com/wxa/submit_audit";
+ String GET_AUDIT_STATUS_URL = "https://api.weixin.qq.com/wxa/get_auditstatus";
+ String GET_LATEST_AUDIT_STATUS_URL = "https://api.weixin.qq.com/wxa/get_latest_auditstatus";
+ String RELEASE_URL = "https://api.weixin.qq.com/wxa/release";
+ String CHANGE_VISIT_STATUS_URL = "https://api.weixin.qq.com/wxa/change_visitstatus";
+ String REVERT_CODE_RELEASE_URL = "https://api.weixin.qq.com/wxa/revertcoderelease";
+ String GET_SUPPORT_VERSION_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/getweappsupportversion";
+ String SET_SUPPORT_VERSION_URL = "https://api.weixin.qq.com/cgi-bin/wxopen/setweappsupportversion";
+ String UNDO_CODE_AUDIT_URL = "https://api.weixin.qq.com/wxa/undocodeaudit";
+
+ /**
+ * 为授权的小程序帐号上传小程序代码(仅仅支持第三方开放平台)
+ *
+ * @param commitRequest 参数
+ * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档
+ */
+ void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException;
+
+ /**
+ * 获取体验小程序的体验二维码
+ *
+ * @return 二维码 bytes
+ * @throws WxErrorException 上传失败时抛出,具体错误码请看类注释文档
+ */
+ byte[] getQrCode() throws WxErrorException;
+
+ /**
+ * 获取授权小程序帐号的可选类目
+ *
+ * @return List
+ * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
+ */
+ List getCategory() throws WxErrorException;
+
+ /**
+ * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用)
+ *
+ * @return page_list 页面配置列表
+ * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档
+ */
+ List getPage() throws WxErrorException;
+
+ /**
+ * 将第三方提交的代码包提交审核(仅供第三方开发者代小程序调用)
+ *
+ * @param auditRequest 提交审核参数
+ * @return 审核编号
+ * @throws WxErrorException 提交失败时返回,具体错误码请看此接口的注释文档
+ */
+ long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException;
+
+ /**
+ * 查询某个指定版本的审核状态(仅供第三方代小程序调用)
+ *
+ * @param auditId 提交审核时获得的审核id
+ * @return 审核状态
+ * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档
+ */
+ WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException;
+
+ /**
+ * 查询最新一次提交的审核状态(仅供第三方代小程序调用)
+ *
+ * @return 审核状态
+ * @throws WxErrorException 查询失败时返回,具体错误码请看此接口的注释文档
+ */
+ WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException;
+
+ /**
+ * 发布已通过审核的小程序(仅供第三方代小程序调用)
+ *
+ * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档
+ */
+ void release() throws WxErrorException;
+
+ /**
+ * 修改小程序线上代码的可见状态(仅供第三方代小程序调用)
+ *
+ * @param action 设置可访问状态,发布后默认可访问,close为不可见,open为可见
+ * @throws WxErrorException 发布失败时抛出,具体错误码请看此接口的注释文档
+ */
+ void changeVisitStatus(String action) throws WxErrorException;
+
+ /**
+ * 小程序版本回退(仅供第三方代小程序调用)
+ *
+ * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
+ */
+ void revertCodeRelease() throws WxErrorException;
+
+ /**
+ * 查询当前设置的最低基础库版本及各版本用户占比 (仅供第三方代小程序调用)
+ *
+ * @return 小程序版本分布信息
+ * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
+ */
+ WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException;
+
+ /**
+ * 设置最低基础库版本(仅供第三方代小程序调用)
+ *
+ * @param version 版本
+ * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
+ */
+ void setSupportVersion(String version) throws WxErrorException;
+
+ /**
+ * 小程序审核撤回
+ * 单个帐号每天审核撤回次数最多不超过1次,一个月不超过10次
+ *
+ * @throws WxErrorException 失败时抛出,具体错误码请看此接口的注释文档
+ */
+ void undoCodeAudit() throws WxErrorException;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
index c437966e..000fda0b 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
@@ -135,6 +135,13 @@ public interface WxMaService {
*/
WxMaTemplateService getTemplateService();
+ /**
+ * 返回代码操作相关的 API
+ *
+ * @return WxMaCodeService
+ */
+ WxMaCodeService getCodeService();
+
/**
* 初始化http请求对象.
*/
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java
new file mode 100644
index 00000000..0cd40f6d
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImpl.java
@@ -0,0 +1,148 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaCodeService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCategory;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+import me.chanjar.weixin.common.bean.result.WxError;
+import me.chanjar.weixin.common.exception.WxErrorException;
+import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
+import me.chanjar.weixin.common.util.http.RequestExecutor;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 20:00
+ */
+public class WxMaCodeServiceImpl implements WxMaCodeService {
+ private static final JsonParser JSON_PARSER = new JsonParser();
+ private WxMaService wxMaService;
+
+ public WxMaCodeServiceImpl(WxMaService wxMaService) {
+ this.wxMaService = wxMaService;
+ }
+
+ @Override
+ public void commit(WxMaCodeCommitRequest commitRequest) throws WxErrorException {
+ this.wxMaService.post(COMMIT_URL, commitRequest.toJson());
+ }
+
+ @Override
+ public byte[] getQrCode() throws WxErrorException {
+ String appId = this.wxMaService.getWxMaConfig().getAppid();
+ Path qrCodeFilePath = null;
+ try {
+ RequestExecutor executor = BaseMediaDownloadRequestExecutor
+ .create(this.wxMaService.getRequestHttp(), Files.createTempDirectory("weixin-java-tools-ma-" + appId).toFile());
+ qrCodeFilePath = this.wxMaService.execute(executor, GET_QRCODE_URL, null).toPath();
+ return Files.readAllBytes(qrCodeFilePath);
+ } catch (IOException e) {
+ throw new WxErrorException(WxError.builder().errorMsg(e.getMessage()).build(), e);
+ } finally {
+ if (qrCodeFilePath != null) {
+ try {
+ // 及时删除二维码文件,避免积压过多缓存文件
+ Files.delete(qrCodeFilePath);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public List getCategory() throws WxErrorException {
+ String responseContent = this.wxMaService.get(GET_CATEGORY_URL, null);
+ JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
+ boolean hasCategoryList = jsonObject.has("category_list");
+ if (hasCategoryList) {
+ return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("category_list"),
+ new TypeToken>() {
+ }.getType());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public List getPage() throws WxErrorException {
+ String responseContent = this.wxMaService.get(GET_PAGE_URL, null);
+ JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
+ boolean hasPageList = jsonObject.has("page_list");
+ if (hasPageList) {
+ return WxMaGsonBuilder.create().fromJson(jsonObject.getAsJsonArray("page_list"),
+ new TypeToken>() {
+ }.getType());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public long submitAudit(WxMaCodeSubmitAuditRequest auditRequest) throws WxErrorException {
+ String responseContent = this.wxMaService.post(SUBMIT_AUDIT_URL, auditRequest.toJson());
+ JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
+ return GsonHelper.getLong(jsonObject, "auditid");
+ }
+
+ @Override
+ public WxMaCodeAuditStatus getAuditStatus(long auditId) throws WxErrorException {
+ JsonObject param = new JsonObject();
+ param.addProperty("auditid", auditId);
+ String responseContent = this.wxMaService.post(GET_AUDIT_STATUS_URL, param.toString());
+ return WxMaCodeAuditStatus.fromJson(responseContent);
+ }
+
+ @Override
+ public WxMaCodeAuditStatus getLatestAuditStatus() throws WxErrorException {
+ String responseContent = this.wxMaService.get(GET_LATEST_AUDIT_STATUS_URL, null);
+ return WxMaCodeAuditStatus.fromJson(responseContent);
+ }
+
+ @Override
+ public void release() throws WxErrorException {
+ this.wxMaService.post(RELEASE_URL, "{}");
+ }
+
+ @Override
+ public void changeVisitStatus(String action) throws WxErrorException {
+ JsonObject param = new JsonObject();
+ param.addProperty("action", action);
+ this.wxMaService.post(CHANGE_VISIT_STATUS_URL, param.toString());
+ }
+
+ @Override
+ public void revertCodeRelease() throws WxErrorException {
+ this.wxMaService.get(REVERT_CODE_RELEASE_URL, null);
+ }
+
+ @Override
+ public WxMaCodeVersionDistribution getSupportVersion() throws WxErrorException {
+ String responseContent = this.wxMaService.post(GET_SUPPORT_VERSION_URL, "{}");
+ return WxMaCodeVersionDistribution.fromJson(responseContent);
+ }
+
+ @Override
+ public void setSupportVersion(String version) throws WxErrorException {
+ JsonObject param = new JsonObject();
+ param.addProperty("version", version);
+ this.wxMaService.post(SET_SUPPORT_VERSION_URL, param.toString());
+ }
+
+ @Override
+ public void undoCodeAudit() throws WxErrorException {
+ this.wxMaService.get(UNDO_CODE_AUDIT_URL, null);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java
index 1ec7801d..d3be4a73 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java
@@ -1,19 +1,6 @@
package cn.binarywang.wx.miniapp.api.impl;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.Lock;
-
-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.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import cn.binarywang.wx.miniapp.api.WxMaCodeService;
import cn.binarywang.wx.miniapp.api.WxMaMediaService;
import cn.binarywang.wx.miniapp.api.WxMaMsgService;
import cn.binarywang.wx.miniapp.api.WxMaQrcodeService;
@@ -35,6 +22,19 @@ import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
+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.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
/**
* @author Binary Wang
@@ -51,6 +51,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttpCharming
+ * @since 2018-04-26 19:44
+ */
+@Data
+@Builder
+public class WxMaCategory implements Serializable {
+ private static final long serialVersionUID = -7663757440028175135L;
+ /**
+ * 一级类目名称
+ */
+ @SerializedName("first_class")
+ private String firstClass;
+ /**
+ * 二级类目名称
+ */
+ @SerializedName("second_class")
+ private String secondClass;
+ /**
+ * 三级类目名称
+ */
+ @SerializedName("third_class")
+ private String thirdClass;
+ /**
+ * 一级类目的ID编号
+ */
+ @SerializedName("first_id")
+ private Long firstId;
+ /**
+ * 二级类目的ID编号
+ */
+ @SerializedName("second_id")
+ private Long secondId;
+ /**
+ * 三级类目的ID编号
+ */
+ @SerializedName("third_id")
+ private Long thirdId;
+
+ /**
+ * 小程序的页面,可通过“获取小程序的第三方提交代码的页面配置”接口获得
+ */
+ private String address;
+ /**
+ * 小程序的标签,多个标签用空格分隔,标签不能多于10个,标签长度不超过20
+ */
+ private String tag;
+ /**
+ * 小程序页面的标题,标题长度不超过32
+ */
+ private String title;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java
new file mode 100644
index 00000000..36c33eaf
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeAuditStatus.java
@@ -0,0 +1,37 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.google.gson.annotations.SerializedName;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 小程序代码审核状态
+ *
+ * @author Charming
+ * @since 2018-04-26 19:44:39
+ */
+@Data
+@Builder
+public class WxMaCodeAuditStatus implements Serializable {
+ private static final long serialVersionUID = 4655119308692217268L;
+ /**
+ * 审核 ID
+ */
+ @SerializedName(value = "auditId", alternate = {"auditid"})
+ private Long auditId;
+ /**
+ * 审核状态,其中0为审核成功,1为审核失败,2为审核中
+ */
+ private Integer status;
+ /**
+ * 当status=1,审核被拒绝时,返回的拒绝原因
+ */
+ private String reason;
+
+ public static WxMaCodeAuditStatus fromJson(String json) {
+ return WxMaGsonBuilder.create().fromJson(json, WxMaCodeAuditStatus.class);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java
new file mode 100644
index 00000000..ec64da59
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequest.java
@@ -0,0 +1,39 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 微信代码请求上传参数
+ *
+ * @author Charming
+ * @since 2018-04-26 19:44:47
+ */
+@Data
+@Builder
+public class WxMaCodeCommitRequest implements Serializable {
+ private static final long serialVersionUID = 7495157056049312108L;
+ /**
+ * 代码库中的代码模版ID
+ */
+ private Long templateId;
+ /**
+ * 第三方自定义的配置
+ */
+ private WxMaCodeExtConfig extConfig;
+ /**
+ * 代码版本号,开发者可自定义
+ */
+ private String userVersion;
+ /**
+ * 代码描述,开发者可自定义
+ */
+ private String userDesc;
+
+ public String toJson() {
+ return WxMaGsonBuilder.create().toJson(this);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java
new file mode 100644
index 00000000..51211123
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeExtConfig.java
@@ -0,0 +1,199 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 上传代码需要用到的第三方自定义的配置
+ *
+ * @author Charming
+ * @since 2018-04-26 19:44
+ */
+@Data
+@Builder
+public class WxMaCodeExtConfig implements Serializable {
+ private static final long serialVersionUID = -7666911367458178753L;
+ /**
+ * 配置 ext.json 是否生效
+ * 必填:是
+ */
+ private boolean extEnable;
+ /**
+ * 配置 extAppid
+ * 必填:是
+ */
+ private String extAppid;
+ /**
+ * 开发自定义的数据字段
+ * 必填:否
+ */
+ private Object ext;
+ /**
+ * 单独设置每个页面的 json
+ * 必填:否
+ * key: page 名称,如 pages/logs/logs
+ * value: page 配置
+ */
+ private Map extPages;
+ /**
+ * 是否直接提交到待审核列表
+ * 必填:否
+ */
+ private Boolean directCommit;
+ /**
+ * 设置页面路径(同 app.json 相同的字段,填写会覆盖 app.json)
+ * 必填:否
+ */
+ private List pages;
+ /**
+ * 设置默认页面的窗口表现(同 app.json 相同的字段,填写会覆盖 app.json)
+ * 必填:否
+ */
+ private PageConfig window;
+ /**
+ * 设置各种网络请求的超时时间(同 app.json 相同的字段,填写会覆盖 app.json)
+ * 必填:否
+ */
+ private NetworkTimeout networkTimeout;
+ /**
+ * 设置是否开启 debug 模式(同 app.json 相同的字段,填写会覆盖 app.json)
+ * 必填:否
+ */
+ private Boolean debug;
+
+ /**
+ * page.json 配置,页面配置
+ * 文档:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/config.html
+ */
+ @Data
+ @Builder
+ public static class PageConfig {
+ /**
+ * 导航栏背景颜色,如"#000000" HexColor
+ * 默认:#000000
+ */
+ private String navigationBarBackgroundColor;
+ /**
+ * 导航栏标题颜色,仅支持 black/white
+ * 默认:white
+ */
+ private String navigationBarTextStyle;
+ /**
+ * 导航栏标题文字内容
+ */
+ private String navigationBarTitleText;
+ /**
+ * 窗口的背景色 HexColor
+ * 默认:#ffffff
+ */
+ private String backgroundColor;
+ /**
+ * 下拉背景字体、loading 图的样式,仅支持 dark/light
+ * 默认:dark
+ */
+ private String backgroundTextStyle;
+ /**
+ * 是否开启下拉刷新,详见页面相关事件处理函数
+ * 默认:false
+ */
+ private String enablePullDownRefresh;
+ /**
+ * 设置为 true 则页面整体不能上下滚动;只在 page.json 中有效,无法在 app.json 中设置该项
+ * 默认:false
+ */
+ private Boolean disableScroll;
+ /**
+ * 页面上拉触底事件触发时距页面底部距离,单位为px
+ * 默认:50
+ */
+ private Integer onReachBottomDistance;
+ }
+
+ /**
+ * tabBar 配置
+ */
+ @Data
+ @Builder
+ public static class TabBar {
+ /**
+ * HexColor, tab 上的文字默认颜色
+ */
+ private String color;
+ /**
+ * HexColor, tab 上的文字选中时的颜色
+ */
+ private String selectedColor;
+ /**
+ * HexColor, tab 的背景色
+ */
+ private String backgroundColor;
+ /**
+ * tabbar 上边框的颜色,仅支持 black/white
+ */
+ private String borderStyle;
+ /**
+ * tab 的列表,最少2个、最多5个 tab
+ */
+ private List- list;
+ /**
+ * 可选值 bottom、top
+ */
+ private String position;
+
+ /**
+ * list item
+ */
+ @Data
+ @Builder
+ public static class Item {
+ /**
+ * 是 页面路径,必须在 pages 中先定义
+ */
+ private String pagePath;
+ /**
+ * tab 上按钮文字
+ */
+ private String text;
+ /**
+ * 图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px,当 postion 为 top 时,此参数无效,不支持网络图片
+ */
+ private String iconPath;
+ /**
+ * 选中时的图片路径,icon 大小限制为40kb,建议尺寸为 81px * 81px ,当 postion 为 top 时,此参数无效
+ */
+ private String selectedIconPath;
+ }
+ }
+
+ /**
+ * 各种网络请求的超时时间
+ */
+ @Data
+ @Builder
+ public static class NetworkTimeout {
+ /**
+ * wx.request的超时时间,单位毫秒,默认为:60000
+ * 必填:否
+ */
+ private Integer request;
+ /**
+ * wx.connectSocket的超时时间,单位毫秒,默认为:60000
+ * 必填:否
+ */
+ private Integer connectSocket;
+ /**
+ * wx.uploadFile的超时时间,单位毫秒,默认为:60000
+ * 必填:否
+ */
+ private Integer uploadFile;
+ /**
+ * wx.downloadFile的超时时间,单位毫秒,默认为:60000
+ * 必填:否
+ */
+ private Integer downloadFile;
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java
new file mode 100644
index 00000000..5b784dbd
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequest.java
@@ -0,0 +1,30 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.google.gson.annotations.SerializedName;
+import lombok.Builder;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 提交审核的请求
+ *
+ * @author Charming
+ * @since 2018-04-26 19:45
+ */
+@Data
+@Builder
+public class WxMaCodeSubmitAuditRequest implements Serializable {
+ private static final long serialVersionUID = 8854979405505241314L;
+ /**
+ * 提交审核项的一个列表(至少填写1项,至多填写5项)
+ */
+ @SerializedName("item_list")
+ private List itemList;
+
+ public String toJson() {
+ return WxMaGsonBuilder.create().toJson(this);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java
new file mode 100644
index 00000000..4c0f0964
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistribution.java
@@ -0,0 +1,30 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * 小程序代码版本号分布
+ *
+ * @author Charming
+ * @since 2018-04-26 19:45
+ */
+@Data
+public class WxMaCodeVersionDistribution {
+ /**
+ * 当前版本
+ */
+ private String nowVersion;
+ /**
+ * 受影响用户占比
+ * key: version, 版本号
+ * value: percentage, 受影响比例
+ */
+ private Map uvInfo;
+
+ public static WxMaCodeVersionDistribution fromJson(String json) {
+ return WxMaGsonBuilder.create().fromJson(json, WxMaCodeVersionDistribution.class);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java
new file mode 100644
index 00000000..01f6496b
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/code/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 微信小程序代码管理相关的 bean
+ *
+ * @author Charming
+ * @since 2018-04-26 19:44
+ */
+package cn.binarywang.wx.miniapp.bean.code;
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java
new file mode 100755
index 00000000..410f86ca
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeCommitRequestGsonAdapter.java
@@ -0,0 +1,28 @@
+package cn.binarywang.wx.miniapp.util.json;
+
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import java.lang.reflect.Type;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 19:47
+ */
+public class WxMaCodeCommitRequestGsonAdapter implements JsonSerializer {
+
+ @Override
+ public JsonElement serialize(WxMaCodeCommitRequest request, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject requestJson = new JsonObject();
+ requestJson.addProperty("template_id", request.getTemplateId());
+ requestJson.addProperty("user_version", request.getUserVersion());
+ requestJson.addProperty("user_desc", request.getUserDesc());
+ if (request.getExtConfig() != null) {
+ requestJson.addProperty("ext_json", WxMaGsonBuilder.create().toJson(request.getExtConfig()));
+ }
+ return requestJson;
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java
new file mode 100644
index 00000000..027ca6a9
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaCodeVersionDistributionGsonAdapter.java
@@ -0,0 +1,50 @@
+package cn.binarywang.wx.miniapp.util.json;
+
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 19:47
+ */
+public class WxMaCodeVersionDistributionGsonAdapter implements JsonDeserializer {
+ @Override
+ public WxMaCodeVersionDistribution deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+ if (json == null) {
+ return null;
+ }
+
+ WxMaCodeVersionDistribution distribution = new WxMaCodeVersionDistribution();
+ JsonObject object = json.getAsJsonObject();
+ distribution.setNowVersion(GsonHelper.getString(object, "now_version"));
+ distribution.setUvInfo(getAsMap(object.getAsJsonObject("uv_info"), "items"));
+ return distribution;
+ }
+
+ private Map getAsMap(JsonObject object, String memberName) {
+ JsonArray array = object.getAsJsonArray(memberName);
+ if (array != null && array.size() > 0) {
+ Map map = new LinkedHashMap<>(array.size());
+ for (JsonElement element : array) {
+ JsonObject elementObject = element.getAsJsonObject();
+ String version = GsonHelper.getString(elementObject, "version");
+ if (version != null) {
+ Float percentage = GsonHelper.getFloat(elementObject, "percentage");
+ map.put(version, percentage);
+ }
+ }
+ return map;
+ }
+ return null;
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java
index 5a08a39a..a11e8e6c 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/json/WxMaGsonBuilder.java
@@ -1,6 +1,8 @@
package cn.binarywang.wx.miniapp.util.json;
import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -13,6 +15,8 @@ public class WxMaGsonBuilder {
static {
INSTANCE.disableHtmlEscaping();
INSTANCE.registerTypeAdapter(WxMaTemplateMessage.class, new WxMaTemplateMessageGsonAdapter());
+ INSTANCE.registerTypeAdapter(WxMaCodeCommitRequest.class, new WxMaCodeCommitRequestGsonAdapter());
+ INSTANCE.registerTypeAdapter(WxMaCodeVersionDistribution.class, new WxMaCodeVersionDistributionGsonAdapter());
}
public static Gson create() {
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java
new file mode 100644
index 00000000..34c78899
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaCodeServiceImplTest.java
@@ -0,0 +1,156 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaCodeService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCategory;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeAuditStatus;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeCommitRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeExtConfig;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeSubmitAuditRequest;
+import cn.binarywang.wx.miniapp.bean.code.WxMaCodeVersionDistribution;
+import cn.binarywang.wx.miniapp.config.WxMaConfig;
+import cn.binarywang.wx.miniapp.test.ApiTestModule;
+import com.google.inject.Inject;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 20:18
+ */
+@Test
+@Guice(modules = ApiTestModule.class)
+public class WxMaCodeServiceImplTest {
+ @Inject
+ private WxMaService wxService;
+ @Inject
+ private WxMaConfig wxMaConfig;
+
+ @Test
+ public void testGetCategory() throws Exception {
+ List categories = wxService.getCodeService().getCategory();
+ System.out.println(String.valueOf(categories));
+ }
+
+ @Test
+ public void testCommit() throws Exception {
+ String themeColor = "#0074d9";
+ String themeFontColor = "#ffffff";
+
+ Map ext = new HashMap<>();
+ ext.put("appName", "xxx");
+ ext.put("verified", true);
+ ext.put("navigationBarBackgroundColor", themeColor);
+ ext.put("navigationBarTextStyle", themeFontColor);
+ ext.put("companyId", 4128);
+ ext.put("companyFullName", "xxx有限公司");
+
+ WxMaCodeService wxMaCodeService = wxService.getCodeService();
+ WxMaCodeCommitRequest commitRequest = WxMaCodeCommitRequest
+ .builder()
+ .templateId(6L)
+ .userVersion("v0.1.0")
+ .userDesc("init")
+ .extConfig(WxMaCodeExtConfig.builder()
+ .extAppid(wxMaConfig.getAppid())
+ .extEnable(true)
+ .ext(ext)
+ .window(
+ WxMaCodeExtConfig.PageConfig
+ .builder()
+ .navigationBarBackgroundColor(themeColor)
+ .navigationBarTextStyle(themeFontColor)
+ .build()
+ )
+ .build())
+ .build();
+ wxMaCodeService.commit(commitRequest);
+ }
+
+ @Test
+ public void testGetQrCode() throws Exception {
+ byte[] qrCode = wxService.getCodeService().getQrCode();
+ assertTrue(qrCode.length > 0);
+ }
+
+ @Test
+ public void testGetPage() throws Exception {
+ List pageList = wxService.getCodeService().getPage();
+ System.out.println(String.valueOf(pageList));
+ }
+
+ @Test
+ public void testSubmitAudit() throws Exception {
+ WxMaCodeSubmitAuditRequest auditRequest = WxMaCodeSubmitAuditRequest
+ .builder()
+ .itemList(Arrays.asList(
+ WxMaCategory
+ .builder()
+ .address("pages/logs/logs")
+ .tag("工具 效率")
+ .firstClass("工具")
+ .firstId(287L)
+ .secondClass("效率")
+ .secondId(616L)
+ .title("日志")
+ .build()
+ )).build();
+ long auditId = wxService.getCodeService().submitAudit(auditRequest);
+ assertTrue(auditId > 0);
+ // 421937937
+ System.out.println(auditId);
+ }
+
+ @Test
+ public void testGetAuditStatus() throws Exception {
+ WxMaCodeAuditStatus auditStatus = wxService.getCodeService().getAuditStatus(421937937L);
+ System.out.println(auditStatus);
+ assertNotNull(auditStatus);
+ }
+
+ @Test
+ public void testGetLatestAuditStatus() throws Exception {
+ WxMaCodeAuditStatus auditStatus = wxService.getCodeService().getLatestAuditStatus();
+ System.out.println(auditStatus);
+ assertNotNull(auditStatus);
+ }
+
+ @Test
+ public void testRelease() throws Exception {
+ wxService.getCodeService().release();
+ }
+
+ @Test
+ public void testChangeVisitStatus() throws Exception {
+ wxService.getCodeService().changeVisitStatus("open");
+ }
+
+ @Test
+ public void testRevertCodeRelease() throws Exception {
+ wxService.getCodeService().revertCodeRelease();
+ }
+
+ @Test
+ public void testGetSupportVersion() throws Exception {
+ WxMaCodeVersionDistribution distribution = wxService.getCodeService().getSupportVersion();
+ System.out.println(distribution);
+ }
+
+ @Test
+ public void testSetSupportVersion() throws Exception {
+ wxService.getCodeService().setSupportVersion("1.2.0");
+ }
+
+ @Test
+ public void testUndoCodeAudit() throws Exception {
+
+ }
+}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java
new file mode 100644
index 00000000..36cf1c48
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeCommitRequestTest.java
@@ -0,0 +1,25 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 19:54
+ */
+public class WxMaCodeCommitRequestTest {
+ @Test
+ public void testToJson() {
+ WxMaCodeCommitRequest commitRequest = WxMaCodeCommitRequest.builder()
+ .templateId(1L)
+ .userVersion("v0.1.0")
+ .userDesc("init")
+ .extConfig(WxMaCodeExtConfig.builder()
+ .extAppid("app123")
+ .extEnable(true)
+ .build())
+ .build();
+ assertEquals(commitRequest.toJson(), "{\"template_id\":1,\"user_version\":\"v0.1.0\",\"user_desc\":\"init\",\"ext_json\":\"{\\\"extEnable\\\":true,\\\"extAppid\\\":\\\"app123\\\"}\"}");
+ }
+}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java
new file mode 100644
index 00000000..1f962087
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeSubmitAuditRequestTest.java
@@ -0,0 +1,30 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 19:55
+ */
+public class WxMaCodeSubmitAuditRequestTest {
+ @Test
+ public void testToJson() {
+ WxMaCodeSubmitAuditRequest request = WxMaCodeSubmitAuditRequest
+ .builder()
+ .itemList(Arrays.asList(
+ WxMaCategory
+ .builder()
+ .address("pages/logs/logs")
+ .tag("工具 效率")
+ .firstClass("工具")
+ .firstId(287L)
+ .secondClass("效率")
+ .secondId(616L)
+ .title("日志")
+ .build()
+ )).build();
+ System.out.println(request.toJson());
+ }
+}
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java
new file mode 100644
index 00000000..da02972e
--- /dev/null
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/bean/code/WxMaCodeVersionDistributionTest.java
@@ -0,0 +1,15 @@
+package cn.binarywang.wx.miniapp.bean.code;
+
+import org.testng.annotations.Test;
+
+/**
+ * @author Charming
+ * @since 2018-04-26 19:58
+ */
+public class WxMaCodeVersionDistributionTest {
+ @Test
+ public void testFromJson() {
+ String json = "{\"errcode\":0,\"errmsg\":\"ok\",\"now_version\":\"1.2.0\",\"uv_info\":{\"items\":[{\"version\":\"0.0.0\",\"percentage\":0},{\"version\":\"1.0.0\",\"percentage\":0},{\"version\":\"1.0.1\",\"percentage\":0},{\"version\":\"1.1.0\",\"percentage\":0},{\"version\":\"1.1.1\",\"percentage\":0},{\"version\":\"1.2.0\",\"percentage\":0},{\"version\":\"1.2.1\",\"percentage\":0},{\"version\":\"1.2.2\",\"percentage\":0},{\"version\":\"1.2.3\",\"percentage\":0},{\"version\":\"1.2.4\",\"percentage\":0},{\"version\":\"1.2.5\",\"percentage\":0},{\"version\":\"1.2.6\",\"percentage\":0},{\"version\":\"1.3.0\",\"percentage\":0},{\"version\":\"1.4.0\",\"percentage\":0},{\"version\":\"1.4.1\",\"percentage\":0},{\"version\":\"1.4.2\",\"percentage\":0},{\"version\":\"1.4.3\",\"percentage\":0},{\"version\":\"1.4.4\",\"percentage\":0},{\"version\":\"1.5.0\",\"percentage\":0},{\"version\":\"1.5.1\",\"percentage\":0},{\"version\":\"1.5.2\",\"percentage\":0},{\"version\":\"1.5.3\",\"percentage\":0},{\"version\":\"1.5.4\",\"percentage\":0},{\"version\":\"1.5.5\",\"percentage\":0},{\"version\":\"1.5.6\",\"percentage\":0},{\"version\":\"1.5.7\",\"percentage\":0},{\"version\":\"1.5.8\",\"percentage\":0},{\"version\":\"1.6.0\",\"percentage\":0.0132},{\"version\":\"1.6.1\",\"percentage\":0.0132},{\"version\":\"1.6.2\",\"percentage\":0.0132},{\"version\":\"1.6.3\",\"percentage\":0.0132},{\"version\":\"1.6.4\",\"percentage\":0.0132},{\"version\":\"1.6.5\",\"percentage\":0.0132},{\"version\":\"1.6.6\",\"percentage\":0.0132},{\"version\":\"1.6.7\",\"percentage\":0.1711},{\"version\":\"1.6.8\",\"percentage\":0.1711},{\"version\":\"1.7.0\",\"percentage\":0.1842},{\"version\":\"1.7.1\",\"percentage\":0.25},{\"version\":\"1.7.2\",\"percentage\":0.5263},{\"version\":\"1.7.3\",\"percentage\":0.5263},{\"version\":\"1.7.4\",\"percentage\":0.6711}]}}\n";
+ System.out.println(WxMaCodeVersionDistribution.fromJson(json));
+ }
+}