diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeJumpService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeJumpService.java new file mode 100644 index 0000000000..81bd0f671c --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeJumpService.java @@ -0,0 +1,56 @@ +package cn.binarywang.wx.miniapp.api; + +import cn.binarywang.wx.miniapp.bean.qrcode.WxMaQrcodeJumpRule; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + * 小程序 URL Link 二维码快速跳转规则管理服务。 + */ +public interface WxMaQrcodeJumpService { + + /** + * 添加二维码快速跳转规则。 + * + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/qr-code-quickly-jump.html + * + * @param rule 规则 + * @return 结果(errmsg/errcode) + */ + String addRule(WxMaQrcodeJumpRule rule) throws WxErrorException; + + /** + * 获取二维码快速跳转规则。 + * + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/get-qr-code-jump-rule.html + * + * @param isDefault 是否查询默认规则 + * @param prefix 路径前缀(最长 32 个字符) + * @return 二维码规则列表 + */ + List getRules(Boolean isDefault, String prefix) throws WxErrorException; + + /** + * 分页获取二维码快速跳转规则列表。 + * + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/get-qr-code-jump-rule-list.html + * + * @param getType 1:查询前缀匹配的规则;2:查询默认规则 + * @param pageNum 页码,从 1 开始 + * @param pageSize 每页条数,最多 20 + * @return 二维码规则列表 + */ + List getRuleList(Integer getType, Integer pageNum, Integer pageSize) throws WxErrorException; + + /** + * 删除二维码快速跳转规则。 + * + * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/delete-qr-code-jump-rule.html + * + * @param prefix 路径前缀 + * @return 结果(errmsg/errcode) + */ + String deleteRule(String prefix) 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 730a8c5840..4e001c6409 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 @@ -472,6 +472,15 @@ WxMaApiResponse execute( */ WxMaLinkService getLinkService(); + /** + * 获取 URL Link 二维码快速跳转规则管理服务对象。 + * + * 文档:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/qr-code-quickly-jump.html + * + * @return 二维码快速跳转规则管理服务对象WxMaQrcodeJumpService + */ + WxMaQrcodeJumpService getQrcodeJumpService(); + /** * 获取电子发票报销方服务接口服务对象。 * diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java index 6ab0293e0c..9d6c2c0fa6 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java @@ -140,6 +140,7 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH new WxMaShopAfterSaleServiceImpl(this); private final WxMaShopDeliveryService shopDeliveryService = new WxMaShopDeliveryServiceImpl(this); private final WxMaLinkService linkService = new WxMaLinkServiceImpl(this); + private final WxMaQrcodeJumpService qrcodeJumpService = new WxMaQrcodeJumpServiceImpl(this); private final WxMaReimburseInvoiceService reimburseInvoiceService = new WxMaReimburseInvoiceServiceImpl(this); private final WxMaDeviceSubscribeService deviceSubscribeService = @@ -788,6 +789,11 @@ public WxMaLinkService getLinkService() { return this.linkService; } + @Override + public WxMaQrcodeJumpService getQrcodeJumpService() { + return this.qrcodeJumpService; + } + @Override public WxMaReimburseInvoiceService getReimburseInvoiceService() { return this.reimburseInvoiceService; diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImpl.java new file mode 100644 index 0000000000..36d4f65545 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImpl.java @@ -0,0 +1,76 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeJumpService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.qrcode.WxMaQrcodeJumpRule; +import cn.binarywang.wx.miniapp.bean.qrcode.WxMaQrcodeJumpRuleListResponse; +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.QrcodeJump.*; +import static me.chanjar.weixin.common.util.json.WxGsonBuilder.create; + +/** + * {@link WxMaQrcodeJumpService} 实现。 + */ +@RequiredArgsConstructor +public class WxMaQrcodeJumpServiceImpl implements WxMaQrcodeJumpService { + private final WxMaService wxMaService; + + @Override + public String addRule(WxMaQrcodeJumpRule rule) throws WxErrorException { + return this.wxMaService.post(QRCODE_JUMP_ADD, create().toJson(rule)); + } + + @Override + public List getRules(Boolean isDefault, String prefix) throws WxErrorException { + final JsonObject request = new JsonObject(); + if (isDefault != null) { + request.addProperty("is_default", isDefault); + } + if (prefix != null) { + request.addProperty("prefix", prefix); + } + + String response = this.wxMaService.post(QRCODE_JUMP_GET, request.toString()); + WxMaQrcodeJumpRuleListResponse result = create().fromJson(response, WxMaQrcodeJumpRuleListResponse.class); + if (result == null || result.getRuleList() == null || result.getRuleList().isEmpty()) { + return Collections.emptyList(); + } + return result.getRuleList(); + } + + @Override + public List getRuleList(Integer getType, Integer pageNum, Integer pageSize) throws WxErrorException { + final JsonObject request = new JsonObject(); + if (getType != null) { + request.addProperty("get_type", getType); + } + if (pageNum != null) { + request.addProperty("page_num", pageNum); + } + if (pageSize != null) { + request.addProperty("page_size", pageSize); + } + + String response = this.wxMaService.post(QRCODE_JUMP_GET_LIST, request.toString()); + WxMaQrcodeJumpRuleListResponse result = create().fromJson(response, WxMaQrcodeJumpRuleListResponse.class); + if (result == null || result.getRuleList() == null || result.getRuleList().isEmpty()) { + return Collections.emptyList(); + } + return result.getRuleList(); + } + + @Override + public String deleteRule(String prefix) throws WxErrorException { + final Map request = new HashMap<>(1); + request.put("prefix", prefix); + return this.wxMaService.post(QRCODE_JUMP_DELETE, create().toJson(request)); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRule.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRule.java new file mode 100644 index 0000000000..1bfdf18513 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRule.java @@ -0,0 +1,58 @@ +package cn.binarywang.wx.miniapp.bean.qrcode; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * URL Link 二维码快速跳转规则。 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaQrcodeJumpRule implements Serializable { + private static final long serialVersionUID = -3450269467817402123L; + + /** + * 跳转链接规则前缀,最多 32 个字符。 + */ + @SerializedName("prefix") + private String prefix; + + /** + * 是否支持子路径匹配。 + */ + @SerializedName("permit_sub_rule") + private Boolean permitSubRule; + + /** + * 跳转版本,1:正式版;2:测试版;3:体验版。 + */ + @SerializedName("open_version") + private Integer openVersion; + + /** + * 正式版跳转页面。 + */ + @SerializedName("path") + private String path; + + /** + * 测试版/体验版可跳转小程序信息。 + */ + @SerializedName("debug_wxa_info") + private List debugWxaInfo; + + /** + * 二维码规则是否失效。 + */ + @SerializedName("is_expire") + private Boolean isExpire; +} + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRuleListResponse.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRuleListResponse.java new file mode 100644 index 0000000000..f279eff5dc --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpRuleListResponse.java @@ -0,0 +1,28 @@ +package cn.binarywang.wx.miniapp.bean.qrcode; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * URL Link 二维码快速跳转规则列表返回值。 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaQrcodeJumpRuleListResponse implements Serializable { + private static final long serialVersionUID = 6706970228943946110L; + + /** + * 规则列表。 + */ + @SerializedName("rule_list") + private List ruleList; +} + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpWxaItem.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpWxaItem.java new file mode 100644 index 0000000000..d9a94d47db --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/qrcode/WxMaQrcodeJumpWxaItem.java @@ -0,0 +1,33 @@ +package cn.binarywang.wx.miniapp.bean.qrcode; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * URL Link 跳转规则中的小程序信息。 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaQrcodeJumpWxaItem implements Serializable { + private static final long serialVersionUID = -675341413130655505L; + + /** + * 小程序 appid。 + */ + @SerializedName("appid") + private String appId; + + /** + * 跳转页面路径。 + */ + @SerializedName("path") + private String path; +} + diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java index 815d47c623..b9c237a115 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java @@ -301,6 +301,16 @@ public interface Link { String QUERY_URLLINK_URL = "https://api.weixin.qq.com/wxa/query_urllink"; } + /** + * URL Link 二维码快速跳转规则管理. + */ + public interface QrcodeJump { + String QRCODE_JUMP_ADD = "https://api.weixin.qq.com/wxaapi/wxaqrcodefast/addcategoryrule"; + String QRCODE_JUMP_GET = "https://api.weixin.qq.com/wxaapi/wxaqrcodefast/getcategory"; + String QRCODE_JUMP_GET_LIST = "https://api.weixin.qq.com/wxaapi/wxaqrcodefast/getcategorybypage"; + String QRCODE_JUMP_DELETE = "https://api.weixin.qq.com/wxaapi/wxaqrcodefast/deletecategoryrule"; + } + public interface ShortLink { String GENERATE_SHORT_LINK_URL = "https://api.weixin.qq.com/wxa/genwxashortlink"; } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImplTest.java new file mode 100644 index 0000000000..e6f438c8cc --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeJumpServiceImplTest.java @@ -0,0 +1,99 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import cn.binarywang.wx.miniapp.api.WxMaQrcodeJumpService; +import cn.binarywang.wx.miniapp.bean.qrcode.WxMaQrcodeJumpRule; +import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.List; + +import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.QrcodeJump.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * {@link WxMaQrcodeJumpServiceImpl} 单元测试。 + */ +public class WxMaQrcodeJumpServiceImplTest { + + private cn.binarywang.wx.miniapp.api.WxMaService wxMaService; + private WxMaQrcodeJumpService qrcodeJumpService; + + @BeforeMethod + public void setUp() { + this.wxMaService = mock(cn.binarywang.wx.miniapp.api.WxMaService.class); + this.qrcodeJumpService = new WxMaQrcodeJumpServiceImpl(this.wxMaService); + } + + @Test + public void testAddRule() throws WxErrorException { + when(this.wxMaService.post(anyString(), anyString())).thenReturn("{\"errcode\":0,\"errmsg\":\"ok\"}"); + + WxMaQrcodeJumpRule rule = WxMaQrcodeJumpRule.builder() + .prefix("/pages/index") + .permitSubRule(true) + .openVersion(1) + .path("pages/index") + .build(); + + String result = this.qrcodeJumpService.addRule(rule); + assertTrue(result.contains("\"errcode\":0")); + verify(this.wxMaService).post(eq(QRCODE_JUMP_ADD), anyString()); + } + + @Test + public void testGetRules() throws WxErrorException { + when(this.wxMaService.post(anyString(), anyString())) + .thenReturn("{\"rule_list\":[{\"prefix\":\"/pages/index\",\"path\":\"pages/index\"}]}"); + + List rules = this.qrcodeJumpService.getRules(false, "/pages"); + + assertNotNull(rules); + assertEquals(rules.size(), 1); + assertEquals(rules.get(0).getPrefix(), "/pages/index"); + assertEquals(rules.get(0).getPath(), "pages/index"); + verify(this.wxMaService).post(eq(QRCODE_JUMP_GET), eq("{\"is_default\":false,\"prefix\":\"/pages\"}")); + } + + @Test + public void testGetRuleList() throws WxErrorException { + when(this.wxMaService.post(anyString(), anyString())) + .thenReturn("{\"rule_list\":[{\"prefix\":\"/pages/index\",\"path\":\"pages/index\"}]}"); + + List rules = this.qrcodeJumpService.getRuleList(1, 1, 20); + + assertNotNull(rules); + assertEquals(rules.size(), 1); + assertEquals(rules.get(0).getPrefix(), "/pages/index"); + verify(this.wxMaService).post(eq(QRCODE_JUMP_GET_LIST), eq("{\"get_type\":1,\"page_num\":1,\"page_size\":20}")); + } + + @Test + public void testGetRuleListWhenNoRules() throws WxErrorException { + when(this.wxMaService.post(anyString(), anyString())).thenReturn("{\"errcode\":0,\"errmsg\":\"ok\"}"); + + List rules = this.qrcodeJumpService.getRuleList(null, null, null); + + assertNotNull(rules); + assertTrue(rules.isEmpty()); + verify(this.wxMaService).post(eq(QRCODE_JUMP_GET_LIST), eq("{}")); + } + + @Test + public void testDeleteRule() throws WxErrorException { + when(this.wxMaService.post(anyString(), anyString())).thenReturn("{\"errcode\":0,\"errmsg\":\"ok\"}"); + + String result = this.qrcodeJumpService.deleteRule("/pages/index"); + + assertTrue(result.contains("\"errcode\":0")); + verify(this.wxMaService).post(eq(QRCODE_JUMP_DELETE), eq("{\"prefix\":\"/pages/index\"}")); + } +} +