* 微信开放平台:1. WxOpenInRedisConfigStorage 支持 JedisPool/JedisSentinelPool 等 Pool<Jedis> 的子类;2. WxOpenInRedisConfigStorage 增加 keyPrefix 以支持可配置的前缀; * 微信开放平台:增加小程序代码模板库管理 * 小程序:增加代码管理相关 APImaster
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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<WxMaCategory> | |||
| * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | |||
| */ | |||
| List<WxMaCategory> getCategory() throws WxErrorException; | |||
| /** | |||
| * 获取小程序的第三方提交代码的页面配置(仅供第三方开发者代小程序调用) | |||
| * | |||
| * @return page_list 页面配置列表 | |||
| * @throws WxErrorException 获取失败时返回,具体错误码请看此接口的注释文档 | |||
| */ | |||
| List<String> 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; | |||
| } | |||
| @@ -135,6 +135,13 @@ public interface WxMaService { | |||
| */ | |||
| WxMaTemplateService getTemplateService(); | |||
| /** | |||
| * 返回代码操作相关的 API | |||
| * | |||
| * @return WxMaCodeService | |||
| */ | |||
| WxMaCodeService getCodeService(); | |||
| /** | |||
| * 初始化http请求对象. | |||
| */ | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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<File, String> 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<WxMaCategory> 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<List<WxMaCategory>>() { | |||
| }.getType()); | |||
| } else { | |||
| return null; | |||
| } | |||
| } | |||
| @Override | |||
| public List<String> 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<List<String>>() { | |||
| }.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); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/binarywang">Binary Wang</a> | |||
| @@ -51,6 +51,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
| private WxMaUserService userService = new WxMaUserServiceImpl(this); | |||
| private WxMaQrcodeService qrCodeService = new WxMaQrcodeServiceImpl(this); | |||
| private WxMaTemplateService templateService = new WxMaTemplateServiceImpl(this); | |||
| private WxMaCodeService codeService = new WxMaCodeServiceImpl(this); | |||
| private int retrySleepMillis = 1000; | |||
| private int maxRetryTimes = 5; | |||
| @@ -290,4 +291,9 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp<CloseableHttpCl | |||
| public WxMaTemplateService getTemplateService() { | |||
| return this.templateService; | |||
| } | |||
| @Override | |||
| public WxMaCodeService getCodeService() { | |||
| return this.codeService; | |||
| } | |||
| } | |||
| @@ -0,0 +1,62 @@ | |||
| package cn.binarywang.wx.miniapp.bean.code; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.Builder; | |||
| import lombok.Data; | |||
| import java.io.Serializable; | |||
| /** | |||
| * 小程序帐号的可选类目,其中 address / tag / title 是提交审核会用到的 | |||
| * | |||
| * @author <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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; | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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<String, PageConfig> extPages; | |||
| /** | |||
| * 是否直接提交到待审核列表 | |||
| * 必填:否 | |||
| */ | |||
| private Boolean directCommit; | |||
| /** | |||
| * 设置页面路径(同 app.json 相同的字段,填写会覆盖 app.json) | |||
| * 必填:否 | |||
| */ | |||
| private List<String> 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<Item> 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; | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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<WxMaCategory> itemList; | |||
| public String toJson() { | |||
| return WxMaGsonBuilder.create().toJson(this); | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @since 2018-04-26 19:45 | |||
| */ | |||
| @Data | |||
| public class WxMaCodeVersionDistribution { | |||
| /** | |||
| * 当前版本 | |||
| */ | |||
| private String nowVersion; | |||
| /** | |||
| * 受影响用户占比 | |||
| * key: version, 版本号 | |||
| * value: percentage, 受影响比例 | |||
| */ | |||
| private Map<String, Float> uvInfo; | |||
| public static WxMaCodeVersionDistribution fromJson(String json) { | |||
| return WxMaGsonBuilder.create().fromJson(json, WxMaCodeVersionDistribution.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| /** | |||
| * 微信小程序代码管理相关的 bean | |||
| * | |||
| * @author <a href="https://github.com/charmingoh">Charming</a> | |||
| * @since 2018-04-26 19:44 | |||
| */ | |||
| package cn.binarywang.wx.miniapp.bean.code; | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @since 2018-04-26 19:47 | |||
| */ | |||
| public class WxMaCodeCommitRequestGsonAdapter implements JsonSerializer<WxMaCodeCommitRequest> { | |||
| @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; | |||
| } | |||
| } | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @since 2018-04-26 19:47 | |||
| */ | |||
| public class WxMaCodeVersionDistributionGsonAdapter implements JsonDeserializer<WxMaCodeVersionDistribution> { | |||
| @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<String, Float> getAsMap(JsonObject object, String memberName) { | |||
| JsonArray array = object.getAsJsonArray(memberName); | |||
| if (array != null && array.size() > 0) { | |||
| Map<String, Float> 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; | |||
| } | |||
| } | |||
| @@ -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() { | |||
| @@ -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 <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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<WxMaCategory> categories = wxService.getCodeService().getCategory(); | |||
| System.out.println(String.valueOf(categories)); | |||
| } | |||
| @Test | |||
| public void testCommit() throws Exception { | |||
| String themeColor = "#0074d9"; | |||
| String themeFontColor = "#ffffff"; | |||
| Map<String, Object> 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<String> 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 { | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package cn.binarywang.wx.miniapp.bean.code; | |||
| import org.testng.annotations.Test; | |||
| import static org.testng.Assert.assertEquals; | |||
| /** | |||
| * @author <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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\\\"}\"}"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package cn.binarywang.wx.miniapp.bean.code; | |||
| import org.testng.annotations.Test; | |||
| import java.util.Arrays; | |||
| /** | |||
| * @author <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package cn.binarywang.wx.miniapp.bean.code; | |||
| import org.testng.annotations.Test; | |||
| /** | |||
| * @author <a href="https://github.com/charmingoh">Charming</a> | |||
| * @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)); | |||
| } | |||
| } | |||