diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java index e6b1ed16a..1dbb9f64c 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSubscribeService.java @@ -1,5 +1,9 @@ package cn.binarywang.wx.miniapp.api; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyRequest; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyResult; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyExtRequest; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyRequest; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; import me.chanjar.weixin.common.bean.subscribemsg.CategoryData; import me.chanjar.weixin.common.bean.subscribemsg.PubTemplateKeyword; @@ -113,4 +117,44 @@ public interface WxMaSubscribeService { */ void sendSubscribeMsg(WxMaSubscribeMessage subscribeMessage) throws WxErrorException; + /** + *
+   * 激活与更新服务卡片
+   *
+   * 详情请见: 激活与更新服务卡片
+   * 接口url格式: POST https://api.weixin.qq.com/wxa/setusernotify?access_token=ACCESS_TOKEN
+   * 
+ * + * @param request 请求参数 + * @throws WxErrorException . + */ + void setUserNotify(WxMaServiceNotifyRequest request) throws WxErrorException; + + /** + *
+   * 更新服务卡片扩展信息
+   *
+   * 详情请见: 更新服务卡片扩展信息
+   * 接口url格式: POST https://api.weixin.qq.com/wxa/setusernotifyext?access_token=ACCESS_TOKEN
+   * 
+ * + * @param request 请求参数 + * @throws WxErrorException . + */ + void setUserNotifyExt(WxMaServiceNotifyExtRequest request) throws WxErrorException; + + /** + *
+   * 查询服务卡片状态
+   *
+   * 详情请见: 查询服务卡片状态
+   * 接口url格式: POST https://api.weixin.qq.com/wxa/getusernotify?access_token=ACCESS_TOKEN
+   * 
+ * + * @param request 请求参数 + * @return 服务卡片状态 + * @throws WxErrorException . + */ + WxMaGetUserNotifyResult getUserNotify(WxMaGetUserNotifyRequest request) throws WxErrorException; + } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java index a7db154a6..edf4d5ba1 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImpl.java @@ -2,6 +2,10 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyRequest; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyResult; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyExtRequest; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyRequest; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.subscribemsg.CategoryData; @@ -89,4 +93,32 @@ public void sendSubscribeMsg(WxMaSubscribeMessage subscribeMessage) throws WxErr throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); } } + + @Override + public void setUserNotify(WxMaServiceNotifyRequest request) throws WxErrorException { + String responseContent = this.service.post(SERVICE_NOTIFY_SET_URL, request.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } + + @Override + public void setUserNotifyExt(WxMaServiceNotifyExtRequest request) throws WxErrorException { + String responseContent = this.service.post(SERVICE_NOTIFY_SET_EXT_URL, request.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + } + + @Override + public WxMaGetUserNotifyResult getUserNotify(WxMaGetUserNotifyRequest request) throws WxErrorException { + String responseContent = this.service.post(SERVICE_NOTIFY_GET_URL, request.toJson()); + JsonObject jsonObject = GsonParser.parse(responseContent); + if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) { + throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp)); + } + return WxMaGsonBuilder.create().fromJson(responseContent, WxMaGetUserNotifyResult.class); + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyRequest.java new file mode 100644 index 000000000..abc7518e0 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyRequest.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 查询服务卡片状态请求. + * + *

接口文档: + * + * 查询服务卡片状态 + * + * @author GitHub Copilot + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaGetUserNotifyRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户身份标识符. + *

+   * 参数:openid
+   * 是否必填:是
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 动态更新令牌. + *
+   * 参数:notify_code
+   * 是否必填:是
+   * 
+ */ + @SerializedName("notify_code") + private String notifyCode; + + /** + * 卡片ID. + *
+   * 参数:notify_type
+   * 是否必填:是
+   * 
+ */ + @SerializedName("notify_type") + private Integer notifyType; + + /** + * 转为 JSON 字符串. + * + * @return JSON 字符串 + */ + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyResult.java new file mode 100644 index 000000000..0090eb19b --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaGetUserNotifyResult.java @@ -0,0 +1,60 @@ +package cn.binarywang.wx.miniapp.bean; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * 查询服务卡片状态响应. + * + *

接口文档: + * + * 查询服务卡片状态 + * + * @author GitHub Copilot + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class WxMaGetUserNotifyResult extends WxMaBaseResponse { + private static final long serialVersionUID = 1L; + + /** + * 卡片状态信息. + */ + @SerializedName("notify_info") + private NotifyInfo notifyInfo; + + /** + * 卡片状态详情. + */ + @Data + public static class NotifyInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 卡片ID. + */ + @SerializedName("notify_type") + private Integer notifyType; + + /** + * 上次有效推送的卡片状态与状态相关字段,没推送过为空字符串. + */ + @SerializedName("content_json") + private String contentJson; + + /** + * code 状态:0 正常;1 有风险;2 异常;10 用户拒收本次code. + */ + @SerializedName("code_state") + private Integer codeState; + + /** + * code 过期时间,秒级时间戳. + */ + @SerializedName("code_expire_time") + private Long codeExpireTime; + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyExtRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyExtRequest.java new file mode 100644 index 000000000..56315ce95 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyExtRequest.java @@ -0,0 +1,82 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 更新服务卡片扩展信息请求. + * + *

接口文档: + * + * 更新服务卡片扩展信息 + * + * @author GitHub Copilot + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaServiceNotifyExtRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户身份标识符. + *

+   * 参数:openid
+   * 是否必填:是
+   * 描述:用户身份标识符。
+   *       当使用微信支付订单号作为 code 时,需要与实际支付用户一致;
+   *       当通过前端获取 code 时,需要与点击 button 的用户一致。
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 卡片ID. + *
+   * 参数:notify_type
+   * 是否必填:是
+   * 描述:卡片ID。
+   * 
+ */ + @SerializedName("notify_type") + private Integer notifyType; + + /** + * 动态更新令牌. + *
+   * 参数:notify_code
+   * 是否必填:是
+   * 描述:动态更新令牌。
+   * 
+ */ + @SerializedName("notify_code") + private String notifyCode; + + /** + * 扩展信息. + *
+   * 参数:ext_json
+   * 是否必填:是
+   * 描述:扩展信息,不同卡片的定义不同。
+   * 
+ */ + @SerializedName("ext_json") + private String extJson; + + /** + * 转为 JSON 字符串. + * + * @return JSON 字符串 + */ + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyRequest.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyRequest.java new file mode 100644 index 000000000..e15e0782f --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaServiceNotifyRequest.java @@ -0,0 +1,93 @@ +package cn.binarywang.wx.miniapp.bean; + +import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 激活与更新服务卡片请求. + * + *

接口文档: + * + * 激活与更新服务卡片 + * + * @author GitHub Copilot + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxMaServiceNotifyRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户身份标识符. + *

+   * 参数:openid
+   * 是否必填:是
+   * 描述:用户身份标识符。
+   *       当使用微信支付订单号作为 code 时,需要与实际支付用户一致;
+   *       当通过前端获取 code 时,需要与点击 button 的用户一致。
+   * 
+ */ + @SerializedName("openid") + private String openid; + + /** + * 卡片ID. + *
+   * 参数:notify_type
+   * 是否必填:是
+   * 描述:卡片ID。
+   * 
+ */ + @SerializedName("notify_type") + private Integer notifyType; + + /** + * 动态更新令牌. + *
+   * 参数:notify_code
+   * 是否必填:是
+   * 描述:动态更新令牌。
+   * 
+ */ + @SerializedName("notify_code") + private String notifyCode; + + /** + * 卡片状态与状态相关字段. + *
+   * 参数:content_json
+   * 是否必填:是
+   * 描述:卡片状态与状态相关字段,不同卡片的定义不同。
+   * 
+ */ + @SerializedName("content_json") + private String contentJson; + + /** + * 微信支付订单号验证字段(可选). + *
+   * 参数:check_json
+   * 是否必填:否
+   * 描述:微信支付订单号验证字段。当将微信支付订单号作为 notify_code 时,在激活时需要传入。
+   * 
+ */ + @SerializedName("check_json") + private String checkJson; + + /** + * 转为 JSON 字符串. + * + * @return JSON 字符串 + */ + public String toJson() { + return WxMaGsonBuilder.create().toJson(this); + } +} 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 40633ea6d..4d20d6b46 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 @@ -358,6 +358,15 @@ public interface Subscribe { /** 发送订阅消息 */ String SUBSCRIBE_MSG_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send"; + + /** 激活与更新服务卡片 */ + String SERVICE_NOTIFY_SET_URL = "https://api.weixin.qq.com/wxa/setusernotify"; + + /** 更新服务卡片扩展信息 */ + String SERVICE_NOTIFY_SET_EXT_URL = "https://api.weixin.qq.com/wxa/setusernotifyext"; + + /** 查询服务卡片状态 */ + String SERVICE_NOTIFY_GET_URL = "https://api.weixin.qq.com/wxa/getusernotify"; } public interface User { diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java index 10993e565..c910d121d 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSubscribeServiceImplTest.java @@ -1,6 +1,11 @@ package cn.binarywang.wx.miniapp.api.impl; import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyRequest; +import cn.binarywang.wx.miniapp.bean.WxMaGetUserNotifyResult; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyExtRequest; +import cn.binarywang.wx.miniapp.bean.WxMaServiceNotifyRequest; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; import me.chanjar.weixin.common.bean.subscribemsg.CategoryData; import me.chanjar.weixin.common.bean.subscribemsg.PubTemplateKeyword; @@ -10,12 +15,16 @@ import com.google.common.collect.Lists; import com.google.inject.Inject; import me.chanjar.weixin.common.error.WxErrorException; +import org.testng.Assert; import org.testng.annotations.Guice; import org.testng.annotations.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * 测试类. @@ -71,4 +80,60 @@ public void testSendSubscribeMsg() throws WxErrorException { // TODO 待完善补充 this.wxService.getSubscribeService().sendSubscribeMsg(WxMaSubscribeMessage.builder().build()); } + + @Test + public void testSetUserNotify() throws WxErrorException { + WxMaService service = mock(WxMaService.class); + when(service.post(anyString(), anyString())).thenReturn("{\"errcode\":0,\"errmsg\":\"ok\"}"); + + WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(service); + WxMaServiceNotifyRequest request = WxMaServiceNotifyRequest.builder() + .openid("test_openid") + .notifyType(1) + .notifyCode("test_notify_code") + .contentJson("{}") + .build(); + subscribeService.setUserNotify(request); + } + + @Test + public void testSetUserNotifyExt() throws WxErrorException { + WxMaService service = mock(WxMaService.class); + when(service.post(anyString(), anyString())).thenReturn("{\"errcode\":0,\"errmsg\":\"ok\"}"); + + WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(service); + WxMaServiceNotifyExtRequest request = WxMaServiceNotifyExtRequest.builder() + .openid("test_openid") + .notifyType(1) + .notifyCode("test_notify_code") + .extJson("{}") + .build(); + subscribeService.setUserNotifyExt(request); + } + + @Test + public void testGetUserNotify() throws WxErrorException { + WxMaService service = mock(WxMaService.class); + when(service.post(anyString(), anyString())).thenReturn( + "{\"errcode\":0,\"errmsg\":\"ok\"," + + "\"notify_info\":{" + + "\"notify_type\":1," + + "\"content_json\":\"{\\\"status\\\":1}\"," + + "\"code_state\":0," + + "\"code_expire_time\":1700000000" + + "}}"); + + WxMaSubscribeService subscribeService = new WxMaSubscribeServiceImpl(service); + WxMaGetUserNotifyRequest request = WxMaGetUserNotifyRequest.builder() + .openid("test_openid") + .notifyCode("test_notify_code") + .notifyType(1) + .build(); + WxMaGetUserNotifyResult result = subscribeService.getUserNotify(request); + Assert.assertNotNull(result); + Assert.assertNotNull(result.getNotifyInfo()); + Assert.assertEquals(result.getNotifyInfo().getNotifyType().intValue(), 1); + Assert.assertEquals(result.getNotifyInfo().getCodeState().intValue(), 0); + Assert.assertEquals(result.getNotifyInfo().getCodeExpireTime().longValue(), 1700000000L); + } }