- 完成ExternalContact基础三个接口 - 外部联系人变更事件的消息解析 - 外部联系人的属性,增加标记公司名称和标记电话号码,还有tag分类 - 企业微信API url修改优化master
| @@ -0,0 +1,58 @@ | |||
| package me.chanjar.weixin.cp.api; | |||
| import me.chanjar.weixin.common.error.WxErrorException; | |||
| import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
| import java.util.List; | |||
| /** | |||
| * <pre> | |||
| * 外部联系人管理接口,企业微信的外部联系人的接口和通讯录接口已经拆离 | |||
| * Created by Joe Cao on 2019/6/14 | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/JoeCao">JoeCao</a> | |||
| */ | |||
| public interface WxCpExternalContactService { | |||
| /** | |||
| * 获取外部联系人详情. | |||
| * <pre> | |||
| * 企业可通过此接口,根据外部联系人的userid,拉取外部联系人详情。权限说明: | |||
| * 企业需要使用外部联系人管理secret所获取的accesstoken来调用 | |||
| * 第三方应用需拥有“企业客户”权限。 | |||
| * 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。 | |||
| * </pre> | |||
| * | |||
| * @param userId 外部联系人的userid | |||
| */ | |||
| WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; | |||
| /** | |||
| * 获取外部联系人列表. | |||
| * <pre> | |||
| * 企业可通过此接口获取指定成员添加的客户列表。 | |||
| * 客户是指配置了客户联系功能的成员所添加的外部联系人。 | |||
| * 没有配置客户联系功能的成员,所添加的外部联系人将不会作为客户返回。 | |||
| * 第三方应用需拥有“企业客户”权限。 | |||
| * 第三方应用调用时,返回的跟进人follow_user仅包含应用可见范围之内的成员。 | |||
| * </pre> | |||
| * | |||
| * @param userId 外部联系人的userid | |||
| * @return List of External wx id | |||
| */ | |||
| List<String> listExternalContacts(String userId) throws WxErrorException; | |||
| /** | |||
| * 企业和第三方服务商可通过此接口获取配置了客户联系功能的成员(Customer Contact)列表。 | |||
| * <pre> | |||
| * 企业需要使用外部联系人管理secret所获取的accesstoken来调用(accesstoken如何获取?); | |||
| * 第三方应用需拥有“企业客户”权限。 | |||
| * 第三方应用只能获取到可见范围内的配置了客户联系功能的成员 | |||
| * </pre> | |||
| * | |||
| * @return List of CpUser id | |||
| */ | |||
| List<String> listFollowUser() throws WxErrorException; | |||
| } | |||
| @@ -284,7 +284,9 @@ public interface WxCpService { | |||
| * 获取用户相关接口的服务类对象 | |||
| */ | |||
| WxCpUserService getUserService(); | |||
| WxCpExternalContactService getExternalContactService(); | |||
| /** | |||
| * 获取群聊服务 | |||
| * | |||
| @@ -149,4 +149,6 @@ public interface WxCpUserService { | |||
| * @param userId 外部联系人的userid | |||
| */ | |||
| WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException; | |||
| } | |||
| @@ -19,17 +19,7 @@ import me.chanjar.weixin.common.util.http.RequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.RequestHttp; | |||
| import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; | |||
| import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; | |||
| import me.chanjar.weixin.cp.api.WxCpAgentService; | |||
| import me.chanjar.weixin.cp.api.WxCpChatService; | |||
| import me.chanjar.weixin.cp.api.WxCpDepartmentService; | |||
| import me.chanjar.weixin.cp.api.WxCpMediaService; | |||
| import me.chanjar.weixin.cp.api.WxCpMenuService; | |||
| import me.chanjar.weixin.cp.api.WxCpOAuth2Service; | |||
| import me.chanjar.weixin.cp.api.WxCpOaService; | |||
| import me.chanjar.weixin.cp.api.WxCpService; | |||
| import me.chanjar.weixin.cp.api.WxCpTagService; | |||
| import me.chanjar.weixin.cp.api.WxCpTaskCardService; | |||
| import me.chanjar.weixin.cp.api.WxCpUserService; | |||
| import me.chanjar.weixin.cp.api.*; | |||
| import me.chanjar.weixin.cp.bean.WxCpMaJsCode2SessionResult; | |||
| import me.chanjar.weixin.cp.bean.WxCpMessage; | |||
| import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; | |||
| @@ -60,6 +50,7 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
| private WxCpAgentService agentService = new WxCpAgentServiceImpl(this); | |||
| private WxCpOaService oaService = new WxCpOaServiceImpl(this); | |||
| private WxCpTaskCardService taskCardService = new WxCpTaskCardServiceImpl(this); | |||
| private WxCpExternalContactService externalContactService = new WxCpExternalContactServiceImpl(this); | |||
| /** | |||
| * 全局的是否正在刷新access token的锁 | |||
| @@ -396,6 +387,11 @@ public abstract class BaseWxCpServiceImpl<H, P> implements WxCpService, RequestH | |||
| return userService; | |||
| } | |||
| @Override | |||
| public WxCpExternalContactService getExternalContactService() { | |||
| return externalContactService; | |||
| } | |||
| @Override | |||
| public WxCpChatService getChatService() { | |||
| return chatService; | |||
| @@ -0,0 +1,41 @@ | |||
| package me.chanjar.weixin.cp.api.impl; | |||
| import me.chanjar.weixin.common.error.WxErrorException; | |||
| import me.chanjar.weixin.cp.api.WxCpExternalContactService; | |||
| import me.chanjar.weixin.cp.api.WxCpService; | |||
| import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo; | |||
| import me.chanjar.weixin.cp.bean.WxCpUserExternalContactList; | |||
| import me.chanjar.weixin.cp.bean.WxCpUserWithExternalPermission; | |||
| import java.util.List; | |||
| import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.ExternalContact.*; | |||
| public class WxCpExternalContactServiceImpl implements WxCpExternalContactService { | |||
| private WxCpService mainService; | |||
| public WxCpExternalContactServiceImpl(WxCpService mainService) { | |||
| this.mainService = mainService; | |||
| } | |||
| @Override | |||
| public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException { | |||
| String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId), null); | |||
| return WxCpUserExternalContactInfo.fromJson(responseContent); | |||
| } | |||
| @Override | |||
| public List<String> listExternalContacts(String userId) throws WxErrorException { | |||
| String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(LIST_EXTERNAL_CONTACT + userId), null); | |||
| WxCpUserExternalContactList list = WxCpUserExternalContactList.fromJson(responseContent); | |||
| return list.getExternalUserId(); | |||
| } | |||
| @Override | |||
| public List<String> listFollowUser() throws WxErrorException { | |||
| String responseContent = this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(GET_FOLLOW_USER_LIST), null); | |||
| WxCpUserWithExternalPermission list = WxCpUserWithExternalPermission.fromJson(responseContent); | |||
| return list.getFollowUser(); | |||
| } | |||
| } | |||
| @@ -1,16 +1,11 @@ | |||
| package me.chanjar.weixin.cp.bean; | |||
| import java.util.List; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import lombok.AllArgsConstructor; | |||
| import lombok.Builder; | |||
| import lombok.Data; | |||
| import lombok.Getter; | |||
| import lombok.NoArgsConstructor; | |||
| import lombok.Setter; | |||
| import lombok.*; | |||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
| import java.util.List; | |||
| /** | |||
| * <pre> | |||
| * 外部联系人详情 | |||
| @@ -118,9 +113,26 @@ public class WxCpUserExternalContactInfo { | |||
| private String description; | |||
| @SerializedName("createtime") | |||
| private Long createTime; | |||
| private String state; | |||
| @SerializedName("remark_company") | |||
| private String remarkCompany; | |||
| @SerializedName("remark_mobiles") | |||
| private String[] remarkMobiles; | |||
| private Tag[] tags; | |||
| } | |||
| public static WxCpUserExternalContactInfo fromJson(String json) { | |||
| return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactInfo.class); | |||
| } | |||
| @Setter | |||
| @Getter | |||
| public static class Tag { | |||
| @SerializedName("group_name") | |||
| private String groupName; | |||
| @SerializedName("tag_name") | |||
| private String tagName; | |||
| private int type; | |||
| } | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| package me.chanjar.weixin.cp.bean; | |||
| import com.google.gson.annotations.Expose; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
| import java.util.List; | |||
| /** | |||
| * <pre> | |||
| * 外部联系人列表 | |||
| * Created by Joe Cao on 2019/6/16. | |||
| * 参考文档:https://work.weixin.qq.com/api/doc#90001/90143/91570 | |||
| * </pre> | |||
| * | |||
| * @author <a href="https://github.com/JoeCao">Joe Cao</a> | |||
| */ | |||
| public class WxCpUserExternalContactList { | |||
| @SerializedName("errcode") | |||
| @Expose | |||
| private Long errcode; | |||
| @SerializedName("errmsg") | |||
| @Expose | |||
| private String errmsg; | |||
| @SerializedName("external_userid") | |||
| @Expose | |||
| private List<String> externalUserId = null; | |||
| public Long getErrcode() { | |||
| return errcode; | |||
| } | |||
| public void setErrcode(Long errcode) { | |||
| this.errcode = errcode; | |||
| } | |||
| public String getErrmsg() { | |||
| return errmsg; | |||
| } | |||
| public void setErrmsg(String errmsg) { | |||
| this.errmsg = errmsg; | |||
| } | |||
| public List<String> getExternalUserId() { | |||
| return externalUserId; | |||
| } | |||
| public void setExternalUserId(List<String> externalUserId) { | |||
| this.externalUserId = externalUserId; | |||
| } | |||
| public static WxCpUserExternalContactList fromJson(String json) { | |||
| return WxCpGsonBuilder.create().fromJson(json, WxCpUserExternalContactList.class); | |||
| } | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package me.chanjar.weixin.cp.bean; | |||
| import com.google.gson.annotations.Expose; | |||
| import com.google.gson.annotations.SerializedName; | |||
| import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | |||
| import java.util.List; | |||
| public class WxCpUserWithExternalPermission { | |||
| @SerializedName("errcode") | |||
| @Expose | |||
| private Long errcode; | |||
| @SerializedName("errmsg") | |||
| @Expose | |||
| private String errmsg; | |||
| @SerializedName("follow_user") | |||
| @Expose | |||
| private List<String> followUser = null; | |||
| public Long getErrcode() { | |||
| return errcode; | |||
| } | |||
| public void setErrcode(Long errcode) { | |||
| this.errcode = errcode; | |||
| } | |||
| public String getErrmsg() { | |||
| return errmsg; | |||
| } | |||
| public void setErrmsg(String errmsg) { | |||
| this.errmsg = errmsg; | |||
| } | |||
| public List<String> getFollowUser() { | |||
| return followUser; | |||
| } | |||
| public void setFollowUser(List<String> followUser) { | |||
| this.followUser = followUser; | |||
| } | |||
| public static WxCpUserWithExternalPermission fromJson(String json) { | |||
| return WxCpGsonBuilder.create().fromJson(json, WxCpUserWithExternalPermission.class); | |||
| } | |||
| } | |||
| @@ -175,6 +175,26 @@ public class WxCpXmlMessage implements Serializable { | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String userId; | |||
| /** | |||
| * 变更信息的外部联系人的userid,注意不是企业成员的帐号. | |||
| */ | |||
| @XStreamAlias("ExternalUserID") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String externalUserID; | |||
| /** | |||
| * 添加此用户的「联系我」方式配置的state参数,可用于识别添加此用户的渠道 | |||
| */ | |||
| @XStreamAlias("State") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String state; | |||
| /** | |||
| * 欢迎语code,可用于发送欢迎语 | |||
| */ | |||
| @XStreamAlias("WelcomeCode") | |||
| @XStreamConverter(value = XStreamCDataConverter.class) | |||
| private String welcomeCode; | |||
| /** | |||
| * 新的UserID,变更时推送(userid由系统生成时可更改一次). | |||
| */ | |||
| @@ -103,4 +103,10 @@ public final class WxCpApiPathConsts { | |||
| public static final String USER_CONVERT_TO_USERID = "/cgi-bin/user/convert_to_userid"; | |||
| public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; | |||
| } | |||
| public static class ExternalContact { | |||
| public static final String GET_EXTERNAL_CONTACT = "/cgi-bin/crm/get_external_contact?external_userid="; | |||
| public static final String LIST_EXTERNAL_CONTACT = "/cgi-bin/externalcontact/list?userid="; | |||
| public static final String GET_FOLLOW_USER_LIST = "/cgi-bin/externalcontact/get_follow_user_list"; | |||
| } | |||
| } | |||
| @@ -89,6 +89,26 @@ public class WxCpConsts { | |||
| */ | |||
| public static final String TASKCARD_CLICK = "taskcard_click"; | |||
| /** | |||
| * 企业成员添加外部联系人事件推送 | |||
| */ | |||
| public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact"; | |||
| } | |||
| /** | |||
| * 企业外部联系人变更事件的CHANGE_TYPE | |||
| */ | |||
| public static class ExternalContactChangeType { | |||
| /** | |||
| * 新增外部联系人 | |||
| */ | |||
| public static final String ADD_EXTERNAL_CONTACT = "add_external_contact"; | |||
| /** | |||
| * 删除外部联系人 | |||
| */ | |||
| public static final String DEL_EXTERNAL_CONTACT = "del_external_contact"; | |||
| } | |||
| /** | |||
| @@ -52,6 +52,8 @@ public class ApiTestModule implements Module { | |||
| protected String tagId; | |||
| protected String externalUserId; | |||
| public String getUserId() { | |||
| return this.userId; | |||
| } | |||
| @@ -76,12 +78,22 @@ public class ApiTestModule implements Module { | |||
| this.tagId = tagId; | |||
| } | |||
| public String getExternalUserId() { | |||
| return externalUserId; | |||
| } | |||
| public void setExternalUserId(String externalUserId) { | |||
| this.externalUserId = externalUserId; | |||
| } | |||
| @Override | |||
| public String toString() { | |||
| return super.toString() + " > WxXmlCpConfigStorage{" + | |||
| "userId='" + this.userId + '\'' + | |||
| ", departmentId='" + this.departmentId + '\'' + | |||
| ", tagId='" + this.tagId + '\'' + | |||
| ", externalUserId='" + this.externalUserId + '\'' + | |||
| '}'; | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| 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.WxCpUserExternalContactInfo; | |||
| import org.testng.annotations.Guice; | |||
| import org.testng.annotations.Test; | |||
| import java.util.List; | |||
| import static org.testng.Assert.assertNotNull; | |||
| @Guice(modules = ApiTestModule.class) | |||
| public class WxCpExternalContactServiceImplTest { | |||
| @Inject | |||
| private WxCpService wxCpService; | |||
| @Inject | |||
| protected ApiTestModule.WxXmlCpInMemoryConfigStorage configStorage; | |||
| private String userId = "someone" + System.currentTimeMillis(); | |||
| @Test | |||
| public void testGetExternalContact() throws WxErrorException { | |||
| String externalUserId = this.configStorage.getExternalUserId(); | |||
| WxCpUserExternalContactInfo result = this.wxCpService.getExternalContactService().getExternalContact(externalUserId); | |||
| System.out.println(result); | |||
| assertNotNull(result); | |||
| } | |||
| @Test | |||
| public void testListExternalContacts() throws WxErrorException { | |||
| String userId = this.configStorage.getUserId(); | |||
| List<String> ret = this.wxCpService.getExternalContactService().listExternalContacts(userId); | |||
| System.out.println(ret); | |||
| assertNotNull(ret); | |||
| } | |||
| @Test | |||
| public void testListExternalWithPermission() throws WxErrorException { | |||
| List<String> ret = this.wxCpService.getExternalContactService().listFollowUser(); | |||
| System.out.println(ret); | |||
| assertNotNull(ret); | |||
| } | |||
| } | |||
| @@ -93,7 +93,7 @@ public class WxCpUserServiceImplTest { | |||
| @Test | |||
| public void testInvite() throws Exception { | |||
| WxCpInviteResult result = this.wxCpService.getUserService().invite( | |||
| Lists.newArrayList(userId), null,null); | |||
| Lists.newArrayList(userId), null, null); | |||
| System.out.println(result); | |||
| } | |||
| @@ -111,10 +111,5 @@ public class WxCpUserServiceImplTest { | |||
| assertNotNull(result); | |||
| } | |||
| @Test | |||
| public void testGetExternalContact() throws WxErrorException { | |||
| WxCpUserExternalContactInfo result = this.wxCpService.getUserService().getExternalContact(userId); | |||
| System.out.println(result); | |||
| assertNotNull(result); | |||
| } | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package me.chanjar.weixin.cp.bean; | |||
| import me.chanjar.weixin.common.api.WxConsts; | |||
| import me.chanjar.weixin.cp.constant.WxCpConsts; | |||
| import org.testng.annotations.Test; | |||
| import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK; | |||
| @@ -106,7 +107,7 @@ public class WxCpXmlMessageTest { | |||
| "<Count>2</Count>" + | |||
| "</SendPicsInfo>" + | |||
| "</xml>"; | |||
| WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>","")); | |||
| WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml.replace("</PicList><PicList>", "")); | |||
| assertEquals(wxMessage.getToUserName(), "wx45a0972125658be9"); | |||
| assertEquals(wxMessage.getFromUserName(), "xiaohe"); | |||
| assertEquals(wxMessage.getCreateTime(), new Long(1502012364L)); | |||
| @@ -173,4 +174,52 @@ public class WxCpXmlMessageTest { | |||
| assertEquals(wxMessage.getEventKey(), "key111"); | |||
| assertEquals(wxMessage.getTaskId(), "taskid111"); | |||
| } | |||
| public void testAddExternalUserEvent() { | |||
| String xml = "<xml>" + | |||
| "<ToUserName><![CDATA[toUser]]></ToUserName>" + | |||
| "<FromUserName><![CDATA[sys]]></FromUserName>" + | |||
| "<CreateTime>1403610513</CreateTime>" + | |||
| "<MsgType><![CDATA[event]]></MsgType>" + | |||
| "<Event><![CDATA[change_external_contact]]></Event>" + | |||
| "<ChangeType><![CDATA[add_external_contact]]></ChangeType>" + | |||
| "<UserID><![CDATA[zhangsan]]></UserID>" + | |||
| "<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" + | |||
| "<State><![CDATA[teststate]]></State>" + | |||
| "<WelcomeCode><![CDATA[WELCOMECODE]]></WelcomeCode>" + | |||
| "</xml >"; | |||
| WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||
| assertEquals(wxMessage.getToUserName(), "toUser"); | |||
| assertEquals(wxMessage.getFromUserName(), "sys"); | |||
| assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); | |||
| assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||
| assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); | |||
| assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.ADD_EXTERNAL_CONTACT); | |||
| assertEquals(wxMessage.getExternalUserID(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); | |||
| assertEquals(wxMessage.getState(), "teststate"); | |||
| assertEquals(wxMessage.getWelcomeCode(), "WELCOMECODE"); | |||
| } | |||
| public void testDelExternalUserEvent() { | |||
| String xml = "<xml>" + | |||
| "<ToUserName><![CDATA[toUser]]></ToUserName>" + | |||
| "<FromUserName><![CDATA[sys]]></FromUserName>" + | |||
| "<CreateTime>1403610513</CreateTime>" + | |||
| "<MsgType><![CDATA[event]]></MsgType>" + | |||
| "<Event><![CDATA[change_external_contact]]></Event>" + | |||
| "<ChangeType><![CDATA[del_external_contact]]></ChangeType>" + | |||
| "<UserID><![CDATA[zhangsan]]></UserID>" + | |||
| "<ExternalUserID><![CDATA[woAJ2GCAAAXtWyujaWJHDDGi0mACH71w]]></ExternalUserID>" + | |||
| "</xml>"; | |||
| WxCpXmlMessage wxMessage = WxCpXmlMessage.fromXml(xml); | |||
| assertEquals(wxMessage.getToUserName(), "toUser"); | |||
| assertEquals(wxMessage.getFromUserName(), "sys"); | |||
| assertEquals(wxMessage.getCreateTime(), Long.valueOf(1403610513L)); | |||
| assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.EVENT); | |||
| assertEquals(wxMessage.getEvent(), WxCpConsts.EventType.CHANGE_EXTERNAL_CONTACT); | |||
| assertEquals(wxMessage.getChangeType(), WxCpConsts.ExternalContactChangeType.DEL_EXTERNAL_CONTACT); | |||
| assertEquals(wxMessage.getUserId(), "zhangsan"); | |||
| assertEquals(wxMessage.getExternalUserID(), "woAJ2GCAAAXtWyujaWJHDDGi0mACH71w"); | |||
| } | |||
| } | |||