diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 557d1fc10..9af7811c8 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -88,6 +88,11 @@ public class WxConsts { * 小程序卡片(要求小程序与公众号已关联) */ public static final String MINIPROGRAMPAGE = "miniprogrampage"; + + /** + * 任务卡片消息 + */ + public static final String TASKCARD = "taskcard"; } /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java index 168bcf749..87892eccd 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/WxCpConsts.java @@ -84,6 +84,11 @@ public class WxCpConsts { */ public static final String LOCATION_SELECT = "location_select"; + /** + * 任务卡片事件推送. + */ + public static final String TASKCARD_CLICK = "taskcard_click"; + } /** diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index 732c1449e..364723db8 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -301,6 +301,13 @@ public interface WxCpService { */ WxCpChatService getChatService(); + /** + * 获取任务卡片服务 + * + * @return 任务卡片服务 + */ + WxCpTaskCardService getTaskCardService(); + WxCpAgentService getAgentService(); WxCpOAService getOAService(); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java new file mode 100644 index 000000000..038c2dd3b --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpTaskCardService.java @@ -0,0 +1,30 @@ +package me.chanjar.weixin.cp.api; + +import me.chanjar.weixin.common.error.WxErrorException; + +import java.util.List; + +/** + *
+ * 任务卡片管理接口. + * Created by Jeff on 2019-05-16. + *+ * + * @author Jeff + * @date 2019-05-16 + */ +public interface WxCpTaskCardService { + /** + *
+ * 更新任务卡片消息状态 + * 详情请见: https://work.weixin.qq.com/api/doc#90000/90135/91579 + * + * 注意: 这个方法使用WxCpConfigStorage里的agentId + *+ * + * @param userIds 企业的成员ID列表 + * @param taskId 任务卡片ID + * @param clickedKey 已点击按钮的Key + */ + void update(List
+ * 任务卡片管理接口. + * Created by Jeff on 2019-05-16. + *+ * + * @author Jeff + * @date 2019-05-16 + */ +@RequiredArgsConstructor +public class WxCpTaskCardServiceImpl implements WxCpTaskCardService { + private final WxCpService mainService; + + @Override + public void update(List
@@ -124,6 +129,7 @@ public class WxCpMessage implements Serializable {
* {@link KefuMsgType#NEWS}
* {@link KefuMsgType#MPNEWS}
* {@link KefuMsgType#MARKDOWN}
+ * {@link KefuMsgType#TASKCARD}
*
*
* @param msgType 消息类型
@@ -249,6 +255,42 @@ public class WxCpMessage implements Serializable {
messageJson.add("mpnews", newsJsonObject);
break;
}
+ case KefuMsgType.TASKCARD: {
+ JsonObject text = new JsonObject();
+ text.addProperty("title", this.getTitle());
+ text.addProperty("description", this.getDescription());
+
+ if (StringUtils.isNotBlank(this.getUrl())) {
+ text.addProperty("url", this.getUrl());
+ }
+
+ text.addProperty("task_id", this.getTaskId());
+
+ JsonArray buttonJsonArray = new JsonArray();
+ for (TaskCardButton button : this.getTaskButtons()) {
+ JsonObject buttonJson = new JsonObject();
+ buttonJson.addProperty("key", button.getKey());
+ buttonJson.addProperty("name", button.getName());
+
+ if (StringUtils.isNotBlank(button.getReplaceName())) {
+ buttonJson.addProperty("replace_name", button.getReplaceName());
+ }
+
+ if (StringUtils.isNotBlank(button.getColor())) {
+ buttonJson.addProperty("color", button.getColor());
+ }
+
+ if (button.getBold() != null) {
+ buttonJson.addProperty("is_bold", button.getBold());
+ }
+
+ buttonJsonArray.add(buttonJson);
+ }
+ text.add("btn", buttonJsonArray);
+
+ messageJson.add("taskcard", text);
+ break;
+ }
default: {
// do nothing
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java
new file mode 100644
index 000000000..c86b255b4
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpTaskCardUpdateResult.java
@@ -0,0 +1,42 @@
+package me.chanjar.weixin.cp.bean;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * + * 更新任务卡片消息状态的返回类 + * 参考文档:https://work.weixin.qq.com/api/doc#90000/90135/91579 + * Created by Jeff on 2019-05-16. + *+ * + * @author Jeff + * @date 2019-05-16 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WxCpTaskCardUpdateResult implements Serializable { + + @SerializedName("errcode") + private Integer errcode; + + @SerializedName("errmsg") + private String errmsg; + + /** + * 用户列表 + */ + @SerializedName("invaliduser") + private List
@@ -157,6 +156,10 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String recognition;
+ @XStreamAlias("TaskId")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String taskId;
+
/**
* 通讯录变更事件.
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java
new file mode 100644
index 000000000..3c2d77924
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/TaskCardBuilder.java
@@ -0,0 +1,68 @@
+package me.chanjar.weixin.cp.bean.messagebuilder;
+
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
+
+import java.util.List;
+
+/**
+ *
+ * 任务卡片消息Builder
+ * 用法: WxCustomMessage m = WxCustomMessage.TASKCARD().title(...)....toUser(...).build();
+ *
+ *
+ * @author Jeff
+ * @date 2019-05-16
+ */
+public class TaskCardBuilder extends BaseBuilder {
+ private String title;
+ private String description;
+ private String url;
+ private String taskId;
+ /**
+ * 按钮个数为1~2个
+ */
+ private List buttons;
+
+ public TaskCardBuilder() {
+ this.msgType = WxConsts.KefuMsgType.TASKCARD;
+ }
+
+ public TaskCardBuilder title(String title) {
+ this.title = title;
+ return this;
+ }
+
+ public TaskCardBuilder description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public TaskCardBuilder url(String url) {
+ this.url = url;
+ return this;
+ }
+
+ public TaskCardBuilder taskId(String taskId) {
+ this.taskId = taskId;
+ return this;
+ }
+
+ public TaskCardBuilder buttons(List buttons) {
+ this.buttons = buttons;
+ return this;
+ }
+
+ @Override
+ public WxCpMessage build() {
+ WxCpMessage m = super.build();
+ m.setSafe(null);
+ m.setTitle(this.title);
+ m.setDescription(this.description);
+ m.setUrl(this.url);
+ m.setTaskId(this.taskId);
+ m.setTaskButtons(this.buttons);
+ return m;
+ }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java
new file mode 100644
index 000000000..0ffe49ce5
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/taskcard/TaskCardButton.java
@@ -0,0 +1,23 @@
+package me.chanjar.weixin.cp.bean.taskcard;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ *
+ * 任务卡片按钮
+ * Created by Jeff on 2019-05-16.
+ *
+ *
+ * @author Jeff
+ * @date 2019-05-16
+ */
+@Data
+@Builder
+public class TaskCardButton {
+ private String key;
+ private String name;
+ private String replaceName;
+ private String color;
+ private Boolean bold;
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java
new file mode 100644
index 000000000..be387548b
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpTaskCardServiceImplTest.java
@@ -0,0 +1,65 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.ApiTestModule;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
+import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+
+import static org.testng.Assert.assertNotNull;
+
+/**
+ * 测试任务卡片服务
+ *
+ * @author Jeff
+ * @date 2019-05-16
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxCpTaskCardServiceImplTest {
+
+ @Inject
+ private WxCpService wxCpService;
+
+ @Test
+ public void testSendTaskCard() throws WxErrorException {
+ TaskCardButton btn1 = TaskCardButton.builder()
+ .key("key1")
+ .name("同意")
+ .replaceName("已同意")
+ .bold(true)
+ .build();
+ TaskCardButton btn2 = TaskCardButton.builder()
+ .key("key2")
+ .name("拒绝")
+ .replaceName("已拒绝")
+ .color("red")
+ .build();
+ WxCpMessage message = WxCpMessage.TASKCARD()
+ .toUser("jeff|mr.t")
+ .title("有一个待审批的请求")
+ .description("申请:购买图书\n金额:100 元")
+ .taskId("task_1")
+ .url("http://www.qq.com")
+ .buttons(Arrays.asList(btn1, btn2))
+ .build();
+
+ WxCpMessageSendResult messageSendResult = this.wxCpService.messageSend(message);
+ assertNotNull(messageSendResult);
+ System.out.println(messageSendResult);
+ System.out.println(messageSendResult.getInvalidPartyList());
+ System.out.println(messageSendResult.getInvalidUserList());
+ System.out.println(messageSendResult.getInvalidTagList());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ wxCpService.getTaskCardService().update(Arrays.asList("jeff", "mr.t"), "task_1", "key1");
+ }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
index 344d1e825..c54211758 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
@@ -1,9 +1,11 @@
package me.chanjar.weixin.cp.bean;
-import org.testng.annotations.*;
-
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
+import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
import static org.assertj.core.api.Assertions.assertThat;
@@ -67,7 +69,7 @@ public class WxCpMessageTest {
WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build();
assertThat(reply.toJson())
- .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" +
+ .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":" +
"[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}," +
"{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}," +
"\"safe\":\"0\"}");
@@ -97,7 +99,7 @@ public class WxCpMessageTest {
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1, article2).build();
assertThat(reply.toJson())
- .isEqualTo( "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" +
+ .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"articles\":" +
"[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," +
"\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}" +
",{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\"," +
@@ -112,4 +114,30 @@ public class WxCpMessageTest {
.isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"},\"safe\":\"0\"}");
}
+ public void testTaskCardBuilder() {
+ TaskCardButton button1 = TaskCardButton.builder()
+ .key("yes")
+ .name("批准")
+ .replaceName("已批准")
+ .color("blue")
+ .bold(true)
+ .build();
+ TaskCardButton button2 = TaskCardButton.builder()
+ .key("yes")
+ .name("拒绝")
+ .replaceName("已拒绝")
+ .color("red")
+ .bold(false)
+ .build();
+ WxCpMessage reply = WxCpMessage.TASKCARD().toUser("OPENID")
+ .title("任务卡片")
+ .description("有一条待处理任务")
+ .url("http://www.qq.com")
+ .taskId("task_123")
+ .buttons(Arrays.asList(button1, button2))
+ .build();
+ assertThat(reply.toJson())
+ .isEqualTo("{\"touser\":\"OPENID\",\"msgtype\":\"taskcard\",\"taskcard\":{\"title\":\"任务卡片\",\"description\":\"有一条待处理任务\",\"url\":\"http://www.qq.com\",\"task_id\":\"task_123\",\"btn\":[{\"key\":\"yes\",\"name\":\"批准\",\"replace_name\":\"已批准\",\"color\":\"blue\",\"is_bold\":true},{\"key\":\"yes\",\"name\":\"拒绝\",\"replace_name\":\"已拒绝\",\"color\":\"red\",\"is_bold\":false}]}}");
+ }
+
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java
index aaf403242..89c0396e0 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpXmlMessageTest.java
@@ -1,9 +1,11 @@
package me.chanjar.weixin.cp.bean;
import me.chanjar.weixin.common.api.WxConsts;
-import org.testng.annotations.*;
+import org.testng.annotations.Test;
-import static org.testng.Assert.*;
+import static me.chanjar.weixin.cp.WxCpConsts.EventType.TASKCARD_CLICK;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
@Test
public class WxCpXmlMessageTest {
@@ -149,4 +151,26 @@ public class WxCpXmlMessageTest {
assertEquals(wxMessage.getExtAttrs().getItems().get(0).getName(), "爱好");
}
+
+ public void testTaskCardEvent() {
+ String xml = "" +
+ " " +
+ " " +
+ "123456789 " +
+ " " +
+ " " +
+ " " +
+ " " +
+ "1 " +
+ " ";
+ WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml);
+ assertEquals(wxMessage.getToUserName(), "toUser");
+ assertEquals(wxMessage.getFromUserName(), "FromUser");
+ assertEquals(wxMessage.getCreateTime(), Long.valueOf(123456789L));
+ assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT);
+ assertEquals(wxMessage.getAgentId(), Integer.valueOf(1));
+ assertEquals(wxMessage.getEvent(), TASKCARD_CLICK);
+ assertEquals(wxMessage.getEventKey(), "key111");
+ assertEquals(wxMessage.getTaskId(), "taskid111");
+ }
}
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java
index 92c298a64..560add604 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/kefu/WxMpKefuMessage.java
@@ -109,6 +109,7 @@ public class WxMpKefuMessage implements Serializable {
* {@link WxConsts.KefuMsgType#MPNEWS}
* {@link WxConsts.KefuMsgType#WXCARD}
* {@link WxConsts.KefuMsgType#MINIPROGRAMPAGE}
+ * {@link WxConsts.KefuMsgType#TASKCARD}
*
*
*/