Unverified Commit 476b0673 authored by Binary Wang's avatar Binary Wang Committed by GitHub
Browse files

Merge pull request #1151 from Wechat-Group/develop

合并 Develop 分支
parents 5570cbab 3bf1bb92
Showing with 1038 additions and 89 deletions
+1038 -89
package me.chanjar.weixin.cp.api.impl;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Maps;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.api.WxCpUserService;
import me.chanjar.weixin.cp.bean.WxCpInviteResult;
import me.chanjar.weixin.cp.bean.WxCpUser;
import me.chanjar.weixin.cp.bean.WxCpUserExternalContactInfo;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.util.List;
import java.util.Map;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.User.*;
/**
* <pre>
* Created by BinaryWang on 2017/6/24.
......@@ -25,59 +25,54 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@RequiredArgsConstructor
public class WxCpUserServiceImpl implements WxCpUserService {
private WxCpService mainService;
public WxCpUserServiceImpl(WxCpService mainService) {
this.mainService = mainService;
}
private final WxCpService mainService;
@Override
public void authenticate(String userId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?userid=" + userId;
this.mainService.get(url, null);
this.mainService.get(this.mainService.getWxCpConfigStorage().getApiUrl(USER_AUTHENTICATE + userId), null);
}
@Override
public void create(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/create";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CREATE);
this.mainService.post(url, user.toJson());
}
@Override
public void update(WxCpUser user) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/update";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_UPDATE);
this.mainService.post(url, user.toJson());
}
@Override
public void delete(String... userIds) throws WxErrorException {
if (userIds.length == 1) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/delete?userid=" + userIds[0];
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_DELETE + userIds[0]);
this.mainService.get(url, null);
return;
}
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete";
JsonObject jsonObject = new JsonObject();
JsonArray jsonArray = new JsonArray();
for (String userid : userIds) {
jsonArray.add(new JsonPrimitive(userid));
for (String userId : userIds) {
jsonArray.add(new JsonPrimitive(userId));
}
jsonObject.add("useridlist", jsonArray);
this.mainService.post(url, jsonObject.toString());
this.mainService.post(USER_BATCH_DELETE, jsonObject.toString());
}
@Override
public WxCpUser getById(String userid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?userid=" + userid;
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_GET + userid);
String responseContent = this.mainService.get(url, null);
return WxCpUser.fromJson(responseContent);
}
@Override
public List<WxCpUser> listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId;
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
......@@ -88,6 +83,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {
params += "&status=0";
}
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
......@@ -99,7 +95,6 @@ public class WxCpUserServiceImpl implements WxCpUserService {
@Override
public List<WxCpUser> listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId;
String params = "";
if (fetchChild != null) {
params += "&fetch_child=" + (fetchChild ? "1" : "0");
......@@ -110,6 +105,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {
params += "&status=0";
}
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_SIMPLE_LIST + departId);
String responseContent = this.mainService.get(url, params);
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return WxCpGsonBuilder.create()
......@@ -122,7 +118,6 @@ public class WxCpUserServiceImpl implements WxCpUserService {
@Override
public WxCpInviteResult invite(List<String> userIds, List<String> partyIds, List<String> tagIds) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/batch/invite";
JsonObject jsonObject = new JsonObject();
if (userIds != null) {
JsonArray jsonArray = new JsonArray();
......@@ -148,12 +143,13 @@ public class WxCpUserServiceImpl implements WxCpUserService {
jsonObject.add("tag", jsonArray);
}
String url = this.mainService.getWxCpConfigStorage().getApiUrl(BATCH_INVITE);
return WxCpInviteResult.fromJson(this.mainService.post(url, jsonObject.toString()));
}
@Override
public Map<String, String> userId2Openid(String userId, Integer agentId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid";
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_OPENID);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("userid", userId);
if (agentId != null) {
......@@ -176,9 +172,19 @@ public class WxCpUserServiceImpl implements WxCpUserService {
@Override
public String openid2UserId(String openid) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid";
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("openid", openid);
String url = this.mainService.getWxCpConfigStorage().getApiUrl(USER_CONVERT_TO_USERID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
}
@Override
public String getUserId(String mobile) throws WxErrorException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("mobile", mobile);
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_USER_ID);
String responseContent = this.mainService.post(url, jsonObject.toString());
JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
return tmpJsonElement.getAsJsonObject().get("userid").getAsString();
......@@ -186,7 +192,7 @@ public class WxCpUserServiceImpl implements WxCpUserService {
@Override
public WxCpUserExternalContactInfo getExternalContact(String userId) throws WxErrorException {
String url = "https://qyapi.weixin.qq.com/cgi-bin/crm/get_external_contact?external_userid=" + userId;
String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_EXTERNAL_CONTACT + userId);
String responseContent = this.mainService.get(url, null);
return WxCpUserExternalContactInfo.fromJson(responseContent);
}
......
......@@ -6,7 +6,7 @@ import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import me.chanjar.weixin.cp.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.constant.WxCpConsts.AppChatMsgType;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
......
......@@ -8,11 +8,15 @@ import me.chanjar.weixin.cp.bean.article.MpnewsArticle;
import me.chanjar.weixin.cp.bean.article.NewArticle;
import me.chanjar.weixin.cp.bean.messagebuilder.*;
import me.chanjar.weixin.cp.bean.taskcard.TaskCardButton;
import me.chanjar.weixin.cp.constant.WxCpConsts;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.KefuMsgType.*;
/**
* 消息.
......@@ -40,9 +44,13 @@ public class WxCpMessage implements Serializable {
private String btnTxt;
private List<NewArticle> articles = new ArrayList<>();
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>();
private String appId;
private String page;
private Boolean emphasisFirstItem;
private Map<String, String> contentItems;
/**
* 任务卡片特有的属性
* 任务卡片特有的属性.
*/
private String taskId;
private List<TaskCardButton> taskButtons = new ArrayList<>();
......@@ -117,10 +125,16 @@ public class WxCpMessage implements Serializable {
return new TaskCardBuilder();
}
/**
* 获得小程序通知消息builder.
*/
public static MiniProgramNoticeMsgBuilder newMiniProgramNoticeBuilder() {
return new MiniProgramNoticeMsgBuilder();
}
/**
* <pre>
* 请使用
* 请使用.
* {@link KefuMsgType#TEXT}
* {@link KefuMsgType#IMAGE}
* {@link KefuMsgType#VOICE}
......@@ -130,6 +144,7 @@ public class WxCpMessage implements Serializable {
* {@link KefuMsgType#MPNEWS}
* {@link KefuMsgType#MARKDOWN}
* {@link KefuMsgType#TASKCARD}
* {@link KefuMsgType#MINIPROGRAM_NOTICE}
* </pre>
*
* @param msgType 消息类型
......@@ -169,19 +184,19 @@ public class WxCpMessage implements Serializable {
private void handleMsgType(JsonObject messageJson) {
switch (this.getMsgType()) {
case KefuMsgType.TEXT: {
case TEXT: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("text", text);
break;
}
case KefuMsgType.MARKDOWN: {
case MARKDOWN: {
JsonObject text = new JsonObject();
text.addProperty("content", this.getContent());
messageJson.add("markdown", text);
break;
}
case KefuMsgType.TEXTCARD: {
case TEXTCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
......@@ -190,25 +205,25 @@ public class WxCpMessage implements Serializable {
messageJson.add("textcard", text);
break;
}
case KefuMsgType.IMAGE: {
case IMAGE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("image", image);
break;
}
case KefuMsgType.FILE: {
case FILE: {
JsonObject image = new JsonObject();
image.addProperty("media_id", this.getMediaId());
messageJson.add("file", image);
break;
}
case KefuMsgType.VOICE: {
case VOICE: {
JsonObject voice = new JsonObject();
voice.addProperty("media_id", this.getMediaId());
messageJson.add("voice", voice);
break;
}
case KefuMsgType.VIDEO: {
case VIDEO: {
JsonObject video = new JsonObject();
video.addProperty("media_id", this.getMediaId());
video.addProperty("thumb_media_id", this.getThumbMediaId());
......@@ -217,7 +232,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("video", video);
break;
}
case KefuMsgType.NEWS: {
case NEWS: {
JsonObject newsJsonObject = new JsonObject();
JsonArray articleJsonArray = new JsonArray();
for (NewArticle article : this.getArticles()) {
......@@ -232,7 +247,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("news", newsJsonObject);
break;
}
case KefuMsgType.MPNEWS: {
case MPNEWS: {
JsonObject newsJsonObject = new JsonObject();
if (this.getMediaId() != null) {
newsJsonObject.addProperty("media_id", this.getMediaId());
......@@ -255,7 +270,7 @@ public class WxCpMessage implements Serializable {
messageJson.add("mpnews", newsJsonObject);
break;
}
case KefuMsgType.TASKCARD: {
case TASKCARD: {
JsonObject text = new JsonObject();
text.addProperty("title", this.getTitle());
text.addProperty("description", this.getDescription());
......@@ -291,6 +306,25 @@ public class WxCpMessage implements Serializable {
messageJson.add("taskcard", text);
break;
}
case MINIPROGRAM_NOTICE: {
JsonObject notice = new JsonObject();
notice.addProperty("appid", this.getAppId());
notice.addProperty("page", this.getPage());
notice.addProperty("description", this.getDescription());
notice.addProperty("title", this.getTitle());
notice.addProperty("emphasis_first_item", this.getEmphasisFirstItem());
JsonArray content = new JsonArray();
for (Map.Entry<String, String> item : this.getContentItems().entrySet()) {
JsonObject articleJson = new JsonObject();
articleJson.addProperty("key", item.getKey());
articleJson.addProperty("value", item.getValue());
content.add(articleJson);
}
notice.add("content_item", content);
messageJson.add("miniprogram_notice", notice);
break;
}
default: {
// do nothing
}
......
package me.chanjar.weixin.cp.bean;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* <pre>
* 用oauth2获取用户信息的结果类
* Created by BinaryWang on 2019/5/26.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class WxCpOauth2UserInfo {
private String openId;
private String deviceId;
private String userId;
private String userTicket;
private String expiresIn;
}
......@@ -29,19 +29,19 @@ public class WxCpTagGetResult implements Serializable {
private String errmsg;
/**
* 用户列表
* 用户列表.
*/
@SerializedName("userlist")
private List<WxCpUser> userlist;
/**
* 部门列表
* 部门列表.
*/
@SerializedName("partylist")
private List<Integer> partylist;
/**
* 标签名称
* 标签名称.
*/
@SerializedName("tagname")
private String tagname;
......
package me.chanjar.weixin.cp.bean;
import java.io.Serializable;
import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
/**
* 微信部门.
*
* @author Daniel Qian
*/
@Data
public class WxCpTpCorp implements Serializable {
private static final long serialVersionUID = -5028321625140879571L;
@SerializedName("corpid")
private String corpId;
@SerializedName("corp_name")
private String corpName;
@SerializedName("corp_full_name")
private String corpFullName;
@SerializedName("corp_type")
private String corpType;
@SerializedName("corp_square_logo_url")
private String corpSquareLogoUrl;
@SerializedName("corp_user_max")
private String corpUserMax;
@SerializedName("permanent_code")
private String permanentCode;
public static WxCpTpCorp fromJson(String json) {
return WxCpGsonBuilder.create().fromJson(json, WxCpTpCorp.class);
}
public String toJson() {
return WxCpGsonBuilder.create().toJson(this);
}
}
package me.chanjar.weixin.cp.bean;
import java.io.Serializable;
import java.util.Map;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.XmlUtils;
import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
/**
* 回调推送的message
* https://work.weixin.qq.com/api/doc#90001/90143/90612
*
* @author zhenjun cai
*/
@XStreamAlias("xml")
@Slf4j
@Data
public class WxCpTpXmlMessage implements Serializable {
private static final long serialVersionUID = 6031833682211475786L;
/**
* 使用dom4j解析的存放所有xml属性和值的map.
*/
private Map<String, Object> allFieldsMap;
@XStreamAlias("SuiteId")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String suiteId;
@XStreamAlias("InfoType")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String infoType;
@XStreamAlias("TimeStamp")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String timeStamp;
@XStreamAlias("SuiteTicket")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String suiteTicket;
@XStreamAlias("AuthCode")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String authCode;
public static WxCpTpXmlMessage fromXml(String xml) {
//修改微信变态的消息内容格式,方便解析
//xml = xml.replace("</PicList><PicList>", "");
final WxCpTpXmlMessage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlMessage.class, xml);
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml));
return xmlPackage;
}
}
package me.chanjar.weixin.cp.bean;
import java.io.Serializable;
import java.util.Map;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamConverter;
import lombok.Data;
import me.chanjar.weixin.common.util.XmlUtils;
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
import me.chanjar.weixin.cp.bean.outxmlbuilder.ImageBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.NewsBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.TextBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.VideoBuilder;
import me.chanjar.weixin.cp.bean.outxmlbuilder.VoiceBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
/**
* 回调消息包.
* https://work.weixin.qq.com/api/doc#90001/90143/91116
*
* @author zhenjun cai
*/
@XStreamAlias("xml")
@Data
public class WxCpTpXmlPackage implements Serializable {
private static final long serialVersionUID = 6031833682211475786L;
/**
* 使用dom4j解析的存放所有xml属性和值的map.
*/
private Map<String, Object> allFieldsMap;
@XStreamAlias("ToUserName")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String toUserName;
@XStreamAlias("AgentID")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String agentId;
@XStreamAlias("Encrypt")
@XStreamConverter(value = XStreamCDataConverter.class)
protected String msgEncrypt;
public static WxCpTpXmlPackage fromXml(String xml) {
//修改微信变态的消息内容格式,方便解析
//xml = xml.replace("</PicList><PicList>", "");
final WxCpTpXmlPackage xmlPackage = XStreamTransformer.fromXml(WxCpTpXmlPackage.class, xml);
xmlPackage.setAllFieldsMap(XmlUtils.xml2Map(xml));
return xmlPackage;
}
}
......@@ -31,6 +31,11 @@ public class WxCpUser implements Serializable {
private Integer status;
private Integer enable;
private Integer isLeader;
/**
* is_leader_in_dept.
* 个数必须和department一致,表示在所在的部门内是否为上级。1表示为上级,0表示非上级。在审批等应用里可以用来标识上级审批人
*/
private Integer[] isLeaderInDept;
private final List<Attr> extAttrs = new ArrayList<>();
private Integer hideMobile;
private String englishName;
......
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;
}
}
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);
}
}
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);
}
}
......@@ -162,7 +162,7 @@ public class WxCpXmlMessage implements Serializable {
/**
* 通讯录变更事件.
* 请参考常量 me.chanjar.weixin.cp.WxCpConsts.ContactChangeType
* 请参考常量 me.chanjar.weixin.cp.constant.WxCpConsts.ContactChangeType
*/
@XStreamAlias("ChangeType")
@XStreamConverter(value = XStreamCDataConverter.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由系统生成时可更改一次).
*/
......@@ -191,11 +211,11 @@ public class WxCpXmlMessage implements Serializable {
private String name;
/**
* 成员部门列表.
* 成员部门列表,变更时推送,仅返回该应用有查看权限的部门id.
*/
@XStreamAlias("Department")
@XStreamConverter(value = XStreamCDataConverter.class)
private String department;
private Long[] departments;
/**
* 手机号码.
......@@ -244,6 +264,12 @@ public class WxCpXmlMessage implements Serializable {
@XStreamAlias("IsLeader")
private Integer isLeader;
/**
* 表示所在部门是否为上级,0-否,1-是,顺序与Department字段的部门逐一对应.
*/
@XStreamAlias("IsLeaderInDept")
private Integer[] isLeaderInDept;
/**
* 座机.
*/
......@@ -268,7 +294,7 @@ public class WxCpXmlMessage implements Serializable {
* 部门Id.
*/
@XStreamAlias("Id")
private Integer id;
private Long id;
/**
* 父部门id.
......
package me.chanjar.weixin.cp.bean.messagebuilder;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import java.util.Map;
/**
* <pre>
* miniprogram_notice 类型的消息 builder
* Created by Binary Wang on 2019/6/16.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class MiniProgramNoticeMsgBuilder extends BaseBuilder<MiniProgramNoticeMsgBuilder> {
private String title;
private String description;
private String appId;
private String page;
private Boolean emphasisFirstItem;
private Map<String, String> contentItems;
public MiniProgramNoticeMsgBuilder() {
this.msgType = WxConsts.KefuMsgType.MINIPROGRAM_NOTICE;
}
public MiniProgramNoticeMsgBuilder appId(String appId) {
this.appId = appId;
return this;
}
public MiniProgramNoticeMsgBuilder page(String page) {
this.page = page;
return this;
}
public MiniProgramNoticeMsgBuilder title(String title) {
this.title = title;
return this;
}
public MiniProgramNoticeMsgBuilder description(String description) {
this.description = description;
return this;
}
public MiniProgramNoticeMsgBuilder contentItems(Map<String, String> contentItems) {
this.contentItems = contentItems;
return this;
}
public MiniProgramNoticeMsgBuilder emphasisFirstItem(Boolean emphasisFirstItem) {
this.emphasisFirstItem = emphasisFirstItem;
return this;
}
@Override
public WxCpMessage build() {
WxCpMessage m = super.build();
m.setContentItems(this.contentItems);
m.setAppId(this.appId);
m.setDescription(this.description);
m.setTitle(this.title);
m.setEmphasisFirstItem(this.emphasisFirstItem);
m.setPage(this.page);
return m;
}
}
......@@ -6,18 +6,32 @@ import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import java.io.File;
/**
* 微信客户端配置存储
* 微信客户端配置存储.
*
* @author Daniel Qian
*/
public interface WxCpConfigStorage {
/**
* 设置企业微信服务器 baseUrl.
* 默认值是 https://qyapi.weixin.qq.com , 如果使用默认值,则不需要调用 setBaseApiUrl
*
* @param baseUrl 企业微信服务器 Url
*/
void setBaseApiUrl(String baseUrl);
/**
* 读取企业微信 API Url.
* 支持私有化企业微信服务器.
*/
String getApiUrl(String path);
String getAccessToken();
boolean isAccessTokenExpired();
/**
* 强制将access token过期掉
* 强制将access token过期掉.
*/
void expireAccessToken();
......@@ -30,12 +44,12 @@ public interface WxCpConfigStorage {
boolean isJsapiTicketExpired();
/**
* 强制将jsapi ticket过期掉
* 强制将jsapi ticket过期掉.
*/
void expireJsapiTicket();
/**
* 应该是线程安全的
* 应该是线程安全的.
*/
void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
......@@ -44,12 +58,12 @@ public interface WxCpConfigStorage {
boolean isAgentJsapiTicketExpired();
/**
* 强制将jsapi ticket过期掉
* 强制将jsapi ticket过期掉.
*/
void expireAgentJsapiTicket();
/**
* 应该是线程安全的
* 应该是线程安全的.
*/
void updateAgentJsapiTicket(String jsapiTicket, int expiresInSeconds);
......@@ -78,7 +92,7 @@ public interface WxCpConfigStorage {
File getTmpDirFile();
/**
* http client builder
* http client builder.
*
* @return ApacheHttpClientBuilder
*/
......
package me.chanjar.weixin.cp.config;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import java.io.File;
/**
* 微信客户端(第三方应用)配置存储
*
* @author zhenjun cai
*/
public interface WxCpTpConfigStorage {
/**
* 设置企业微信服务器 baseUrl.
* 默认值是 https://qyapi.weixin.qq.com , 如果使用默认值,则不需要调用 setBaseApiUrl
*
* @param baseUrl 企业微信服务器 Url
*/
void setBaseApiUrl(String baseUrl);
/**
* 读取企业微信 API Url.
* 支持私有化企业微信服务器.
*/
String getApiUrl(String path);
String getSuiteAccessToken();
boolean isSuiteAccessTokenExpired();
/**
* 强制将suite access token过期掉.
*/
void expireSuiteAccessToken();
void updateSuiteAccessToken(WxAccessToken suiteAccessToken);
void updateSuiteAccessToken(String suiteAccessToken, int expiresIn);
String getSuiteTicket();
boolean isSuiteTicketExpired();
/**
* 强制将suite ticket过期掉.
*/
void expireSuiteTicket();
/**
* 应该是线程安全的.
*/
void updateSuiteTicket(String suiteTicket, int expiresInSeconds);
String getCorpId();
String getCorpSecret();
String getSuiteId();
String getSuiteSecret();
String getToken();
String getAesKey();
long getExpiresTime();
String getHttpProxyHost();
int getHttpProxyPort();
String getHttpProxyUsername();
String getHttpProxyPassword();
File getTmpDirFile();
/**
* http client builder.
*
* @return ApacheHttpClientBuilder
*/
ApacheHttpClientBuilder getApacheHttpClientBuilder();
}
package me.chanjar.weixin.cp.config;
package me.chanjar.weixin.cp.config.impl;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.File;
import java.io.Serializable;
/**
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化.
*
* @author Daniel Qian
*/
public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
public class WxCpDefaultConfigImpl implements WxCpConfigStorage, Serializable {
private static final long serialVersionUID = 1154541446729462780L;
protected volatile String corpId;
protected volatile String corpSecret;
private volatile String corpId;
private volatile String corpSecret;
protected volatile String token;
private volatile String token;
protected volatile String accessToken;
protected volatile String aesKey;
private volatile String aesKey;
protected volatile Integer agentId;
protected volatile long expiresTime;
private volatile long expiresTime;
protected volatile String oauth2redirectUri;
private volatile String oauth2redirectUri;
protected volatile String httpProxyHost;
protected volatile int httpProxyPort;
protected volatile String httpProxyUsername;
protected volatile String httpProxyPassword;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
private volatile String httpProxyPassword;
protected volatile String jsapiTicket;
protected volatile long jsapiTicketExpiresTime;
private volatile String jsapiTicket;
private volatile long jsapiTicketExpiresTime;
protected volatile String agentJsapiTicket;
protected volatile long agentJsapiTicketExpiresTime;
private volatile String agentJsapiTicket;
private volatile long agentJsapiTicketExpiresTime;
protected volatile File tmpDirFile;
private volatile File tmpDirFile;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
private volatile String baseApiUrl;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}
@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL;
}
return baseApiUrl + path;
}
@Override
public String getAccessToken() {
return this.accessToken;
......
package me.chanjar.weixin.cp.config;
package me.chanjar.weixin.cp.config.impl;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.constant.WxCpApiPathConsts;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
......@@ -16,7 +18,7 @@ import java.io.File;
*
* @author gaigeshen
*/
public class WxCpJedisConfigStorage implements WxCpConfigStorage {
public class WxCpRedisConfigImpl implements WxCpConfigStorage {
private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
......@@ -38,23 +40,38 @@ public class WxCpJedisConfigStorage implements WxCpConfigStorage {
private volatile File tmpDirFile;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
public WxCpJedisConfigStorage(JedisPool jedisPool) {
protected volatile String baseApiUrl;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}
@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = WxCpApiPathConsts.DEFAULT_CP_BASE_URL;
}
return baseApiUrl + path;
}
public WxCpRedisConfigImpl(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
public WxCpJedisConfigStorage(String host, int port) {
public WxCpRedisConfigImpl(String host, int port) {
jedisPool = new JedisPool(host, port);
}
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port) {
jedisPool = new JedisPool(poolConfig, host, port);
}
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password) {
jedisPool = new JedisPool(poolConfig, host, port, timeout, password);
}
public WxCpJedisConfigStorage(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) {
public WxCpRedisConfigImpl(JedisPoolConfig poolConfig, String host, int port, int timeout, String password, int database) {
jedisPool = new JedisPool(poolConfig, host, port, timeout, password, database);
}
......
package me.chanjar.weixin.cp.config.impl;
import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.cp.config.WxCpTpConfigStorage;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import java.io.File;
import java.io.Serializable;
/**
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化.
*
* @author someone
*/
public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializable {
private static final long serialVersionUID = 6678780920621872824L;
private volatile String corpId;
private volatile String corpSecret;
private volatile String suiteId;
private volatile String suiteSecret;
private volatile String token;
private volatile String suiteAccessToken;
private volatile String aesKey;
private volatile long expiresTime;
private volatile String oauth2redirectUri;
private volatile String httpProxyHost;
private volatile int httpProxyPort;
private volatile String httpProxyUsername;
private volatile String httpProxyPassword;
private volatile String suiteTicket;
private volatile long suiteTicketExpiresTime;
private volatile File tmpDirFile;
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
private volatile String baseApiUrl;
@Override
public void setBaseApiUrl(String baseUrl) {
this.baseApiUrl = baseUrl;
}
@Override
public String getApiUrl(String path) {
if (baseApiUrl == null) {
baseApiUrl = "https://qyapi.weixin.qq.com";
}
return baseApiUrl + path;
}
@Override
public String getSuiteAccessToken() {
return this.suiteAccessToken;
}
public void setSuiteAccessToken(String suiteAccessToken) {
this.suiteAccessToken = suiteAccessToken;
}
@Override
public boolean isSuiteAccessTokenExpired() {
return System.currentTimeMillis() > this.expiresTime;
}
@Override
public void expireSuiteAccessToken() {
this.expiresTime = 0;
}
@Override
public synchronized void updateSuiteAccessToken(WxAccessToken suiteAccessToken) {
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
}
@Override
public synchronized void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) {
this.suiteAccessToken = suiteAccessToken;
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}
@Override
public String getCorpId() {
return this.corpId;
}
public void setCorpId(String corpId) {
this.corpId = corpId;
}
@Override
public String getCorpSecret() {
return this.corpSecret;
}
public void setCorpSecret(String corpSecret) {
this.corpSecret = corpSecret;
}
@Override
public String getSuiteTicket() {
return this.suiteTicket;
}
public void setSuiteTicket(String suiteTicket) {
this.suiteTicket = suiteTicket;
}
public long getSuiteTicketExpiresTime() {
return this.suiteTicketExpiresTime;
}
public void setSuiteTicketExpiresTime(long suiteTicketExpiresTime) {
this.suiteTicketExpiresTime = suiteTicketExpiresTime;
}
@Override
public boolean isSuiteTicketExpired() {
return System.currentTimeMillis() > this.suiteTicketExpiresTime;
}
@Override
public synchronized void updateSuiteTicket(String suiteTicket, int expiresInSeconds) {
this.suiteTicket = suiteTicket;
// 预留200秒的时间
this.suiteTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
}
@Override
public void expireSuiteTicket() {
this.suiteTicketExpiresTime = 0;
}
@Override
public String getSuiteId() {
return this.suiteId;
}
public void setSuiteId(String corpId) {
this.suiteId = corpId;
}
@Override
public String getSuiteSecret() {
return this.suiteSecret;
}
public void setSuiteSecret(String corpSecret) {
this.suiteSecret = corpSecret;
}
@Override
public String getToken() {
return this.token;
}
public void setToken(String token) {
this.token = token;
}
@Override
public long getExpiresTime() {
return this.expiresTime;
}
public void setExpiresTime(long expiresTime) {
this.expiresTime = expiresTime;
}
@Override
public String getAesKey() {
return this.aesKey;
}
public void setAesKey(String aesKey) {
this.aesKey = aesKey;
}
public void setOauth2redirectUri(String oauth2redirectUri) {
this.oauth2redirectUri = oauth2redirectUri;
}
@Override
public String getHttpProxyHost() {
return this.httpProxyHost;
}
public void setHttpProxyHost(String httpProxyHost) {
this.httpProxyHost = httpProxyHost;
}
@Override
public int getHttpProxyPort() {
return this.httpProxyPort;
}
public void setHttpProxyPort(int httpProxyPort) {
this.httpProxyPort = httpProxyPort;
}
@Override
public String getHttpProxyUsername() {
return this.httpProxyUsername;
}
public void setHttpProxyUsername(String httpProxyUsername) {
this.httpProxyUsername = httpProxyUsername;
}
@Override
public String getHttpProxyPassword() {
return this.httpProxyPassword;
}
public void setHttpProxyPassword(String httpProxyPassword) {
this.httpProxyPassword = httpProxyPassword;
}
@Override
public String toString() {
return WxCpGsonBuilder.create().toJson(this);
}
@Override
public File getTmpDirFile() {
return this.tmpDirFile;
}
public void setTmpDirFile(File tmpDirFile) {
this.tmpDirFile = tmpDirFile;
}
@Override
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
return this.apacheHttpClientBuilder;
}
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
this.apacheHttpClientBuilder = apacheHttpClientBuilder;
}
}
package me.chanjar.weixin.cp.constant;
/**
* <pre>
* 企业微信api地址常量类
* Created by BinaryWang on 2019-06-02.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public final class WxCpApiPathConsts {
public static final String DEFAULT_CP_BASE_URL = "https://qyapi.weixin.qq.com";
public static final String GET_JSAPI_TICKET = "/cgi-bin/get_jsapi_ticket";
public static final String GET_AGENT_CONFIG_TICKET = "/cgi-bin/ticket/get?&type=agent_config";
public static final String MESSAGE_SEND = "/cgi-bin/message/send";
public static final String GET_CALLBACK_IP = "/cgi-bin/getcallbackip";
public static final String BATCH_REPLACE_PARTY = "/cgi-bin/batch/replaceparty";
public static final String BATCH_REPLACE_USER = "/cgi-bin/batch/replaceuser";
public static final String BATCH_GET_RESULT = "/cgi-bin/batch/getresult?jobid=";
public static final String JSCODE_TO_SESSION = "/cgi-bin/miniprogram/jscode2session";
public static final String GET_TOKEN = "/cgi-bin/gettoken?&corpid=%s&corpsecret=%s";
public static class Agent {
public static final String AGENT_GET = "/cgi-bin/agent/get?agentid=%d";
public static final String AGENT_SET = "/cgi-bin/agent/set";
public static final String AGENT_LIST = "/cgi-bin/agent/list";
}
public static class OAuth2 {
public static final String GET_USER_INFO = "/cgi-bin/user/getuserinfo?code=%s&agentid=%d";
public static final String GET_USER_DETAIL = "/cgi-bin/user/getuserdetail";
public static final String URL_OAUTH2_AUTHORIZE = "https://open.weixin.qq.com/connect/oauth2/authorize";
}
public static class Chat {
public static final String APPCHAT_CREATE = "/cgi-bin/appchat/create";
public static final String APPCHAT_UPDATE = "/cgi-bin/appchat/update";
public static final String APPCHAT_GET_CHATID = "/cgi-bin/appchat/get?chatid=";
public static final String APPCHAT_SEND = "/cgi-bin/appchat/send";
}
public static class Department {
public static final String DEPARTMENT_CREATE = "/cgi-bin/department/create";
public static final String DEPARTMENT_UPDATE = "/cgi-bin/department/update";
public static final String DEPARTMENT_DELETE = "/cgi-bin/department/delete?id=%d";
public static final String DEPARTMENT_LIST = "/cgi-bin/department/list";
}
public static class Media {
public static final String MEDIA_GET = "/cgi-bin/media/get";
public static final String MEDIA_UPLOAD = "/cgi-bin/media/upload?type=";
public static final String IMG_UPLOAD = "/cgi-bin/media/uploadimg";
public static final String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk";
}
public static class Menu {
public static final String MENU_CREATE = "/cgi-bin/menu/create?agentid=%d";
public static final String MENU_DELETE = "/cgi-bin/menu/delete?agentid=%d";
public static final String MENU_GET = "/cgi-bin/menu/get?agentid=%d";
}
public static class Oa {
public static final String GET_CHECKIN_DATA = "/cgi-bin/checkin/getcheckindata";
public static final String GET_CHECKIN_OPTION = "/cgi-bin/checkin/getcheckinoption";
public static final String GET_APPROVAL_DATA = "/cgi-bin/corp/getapprovaldata";
public static final String GET_DIAL_RECORD = "/cgi-bin/dial/get_dial_record";
}
public static class Tag {
public static final String TAG_CREATE = "/cgi-bin/tag/create";
public static final String TAG_UPDATE = "/cgi-bin/tag/update";
public static final String TAG_DELETE = "/cgi-bin/tag/delete?tagid=%s";
public static final String TAG_LIST = "/cgi-bin/tag/list";
public static final String TAG_GET = "/cgi-bin/tag/get?tagid=%s";
public static final String TAG_ADD_TAG_USERS = "/cgi-bin/tag/addtagusers";
public static final String TAG_DEL_TAG_USERS = "/cgi-bin/tag/deltagusers";
}
public static class TaskCard {
public static final String UPDATE_TASK_CARD = "/cgi-bin/message/update_taskcard";
}
public static class Tp {
public static final String JSCODE_TO_SESSION = "/cgi-bin/service/miniprogram/jscode2session";
public static final String GET_CORP_TOKEN = "/cgi-bin/service/get_corp_token";
public static final String GET_PERMANENT_CODE = "/cgi-bin/service/get_permanent_code";
public static final String GET_SUITE_TOKEN = "/cgi-bin/service/get_suite_token";
}
public static class User {
public static final String USER_AUTHENTICATE = "/cgi-bin/user/authsucc?userid=";
public static final String USER_CREATE = "/cgi-bin/user/create";
public static final String USER_UPDATE = "/cgi-bin/user/update";
public static final String USER_DELETE = "/cgi-bin/user/delete?userid=";
public static final String USER_BATCH_DELETE = "/cgi-bin/user/batchdelete";
public static final String USER_GET = "/cgi-bin/user/get?userid=";
public static final String USER_LIST = "/cgi-bin/user/list?department_id=";
public static final String USER_SIMPLE_LIST = "/cgi-bin/user/simplelist?department_id=";
public static final String BATCH_INVITE = "/cgi-bin/batch/invite";
public static final String USER_CONVERT_TO_OPENID = "/cgi-bin/user/convert_to_openid";
public static final String USER_CONVERT_TO_USERID = "/cgi-bin/user/convert_to_userid";
public static final String GET_USER_ID = "/cgi-bin/user/getuserid";
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";
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment