- 完成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"); | |||
} | |||
} |