mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-02-17 21:56:28 +08:00
🎨 #1867【企业微信】优化完善第三方应用的接入代码
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
package me.chanjar.weixin.common.util.xml;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.thoughtworks.xstream.converters.basic.StringConverter;
|
||||
|
||||
|
||||
/**
|
||||
* String 数组转换
|
||||
* @author chily.lin
|
||||
*/
|
||||
public class StringArrayConverter extends StringConverter {
|
||||
@Override
|
||||
public boolean canConvert(Class type) {
|
||||
return type == String[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(Object obj) {
|
||||
return "<![CDATA[" + Joiner.on(",").join((String[]) obj) + "]]>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromString(String str) {
|
||||
final Iterable<String> iterable = Splitter.on(",").split(str);
|
||||
String[] results = Iterables.toArray(iterable, String.class);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package me.chanjar.weixin.cp.bean.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
import com.thoughtworks.xstream.annotations.XStreamConverter;
|
||||
import com.thoughtworks.xstream.converters.basic.IntConverter;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.chanjar.weixin.common.util.XmlUtils;
|
||||
import me.chanjar.weixin.common.util.xml.IntegerArrayConverter;
|
||||
import me.chanjar.weixin.common.util.xml.StringArrayConverter;
|
||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
|
||||
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
|
||||
|
||||
@@ -52,6 +56,359 @@ public class WxCpTpXmlMessage implements Serializable {
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String authCorpId;
|
||||
|
||||
@XStreamAlias("ChangeType")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String changeType;
|
||||
|
||||
@XStreamAlias("UserID")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String userID;
|
||||
|
||||
@XStreamAlias("Department")
|
||||
@XStreamConverter(value = IntegerArrayConverter.class)
|
||||
protected Integer[] department;
|
||||
|
||||
@XStreamAlias("MainDepartment")
|
||||
@XStreamConverter(value = IntConverter.class)
|
||||
protected Integer mainDepartment;
|
||||
|
||||
@XStreamAlias("IsLeaderInDept")
|
||||
@XStreamConverter(value = IntegerArrayConverter.class)
|
||||
protected Integer[] isLeaderInDept;
|
||||
|
||||
@XStreamAlias("Mobile")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String mobile;
|
||||
|
||||
@XStreamAlias("Position")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String position;
|
||||
|
||||
@XStreamAlias("Gender")
|
||||
@XStreamConverter(value = IntConverter.class)
|
||||
protected Integer gender;
|
||||
|
||||
@XStreamAlias("Email")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String email;
|
||||
|
||||
@XStreamAlias("Status")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String status;
|
||||
|
||||
@XStreamAlias("Avatar")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String avatar;
|
||||
|
||||
@XStreamAlias("Alias")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String alias;
|
||||
|
||||
@XStreamAlias("Telephone")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String telephone;
|
||||
|
||||
@XStreamAlias("Id")
|
||||
@XStreamConverter(value = IntConverter.class)
|
||||
protected Integer id;
|
||||
|
||||
@XStreamAlias("Name")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String name;
|
||||
|
||||
@XStreamAlias("ParentId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String parentId;
|
||||
|
||||
@XStreamAlias("Order")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected Integer order;
|
||||
|
||||
@XStreamAlias("TagId")
|
||||
@XStreamConverter(value = IntConverter.class)
|
||||
protected Integer tagId;
|
||||
|
||||
@XStreamAlias("AddUserItems")
|
||||
@XStreamConverter(value = StringArrayConverter.class)
|
||||
protected String[] addUserItems;
|
||||
|
||||
@XStreamAlias("DelUserItems")
|
||||
@XStreamConverter(value = StringArrayConverter.class)
|
||||
protected String[] delUserItems;
|
||||
|
||||
@XStreamAlias("AddPartyItems")
|
||||
@XStreamConverter(value = IntegerArrayConverter.class)
|
||||
protected Integer[] addPartyItems;
|
||||
|
||||
@XStreamAlias("DelPartyItems")
|
||||
@XStreamConverter(value = IntegerArrayConverter.class)
|
||||
protected Integer[] delPartyItems;
|
||||
|
||||
//ref: https://work.weixin.qq.com/api/doc/90001/90143/90585
|
||||
@XStreamAlias("ServiceCorpId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String serviceCorpId;
|
||||
|
||||
@XStreamAlias("RegisterCode")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String registerCode;
|
||||
|
||||
@XStreamAlias("ContactSync")
|
||||
protected ContactSync contactSync;
|
||||
|
||||
@XStreamAlias("AuthUserInfo")
|
||||
protected AuthUserInfo authUserInfo;
|
||||
|
||||
@XStreamAlias("TemplateId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String templateId;
|
||||
|
||||
@XStreamAlias("CreateTime")
|
||||
protected Long createTime;
|
||||
|
||||
@XStreamAlias("ToUserName")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String toUserName;
|
||||
|
||||
@XStreamAlias("FromUserName")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String fromUserName;
|
||||
|
||||
@XStreamAlias("MsgType")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String msgType;
|
||||
|
||||
@XStreamAlias("Event")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String event;
|
||||
|
||||
@XStreamAlias("BatchJob")
|
||||
protected BatchJob batchJob;
|
||||
|
||||
@XStreamAlias("ExternalUserID")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String externalUserID;
|
||||
|
||||
@XStreamAlias("WelcomeCode")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String welcomeCode;
|
||||
|
||||
@XStreamAlias("FromUser")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String fromUser;
|
||||
|
||||
@XStreamAlias("Content")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String content;
|
||||
|
||||
@XStreamAlias("MsgId")
|
||||
protected String msgId;
|
||||
|
||||
@XStreamAlias("AgentID")
|
||||
protected Integer agentID;
|
||||
|
||||
@XStreamAlias("PicUrl")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String picUrl;
|
||||
|
||||
@XStreamAlias("MediaId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String mediaId;
|
||||
|
||||
@XStreamAlias("Format")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String format;
|
||||
|
||||
@XStreamAlias("ThumbMediaId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String thumbMediaId;
|
||||
|
||||
@XStreamAlias("Location_X")
|
||||
private Double locationX;
|
||||
|
||||
@XStreamAlias("Location_Y")
|
||||
private Double locationY;
|
||||
|
||||
@XStreamAlias("Scale")
|
||||
private Double scale;
|
||||
|
||||
@XStreamAlias("Label")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String label;
|
||||
|
||||
@XStreamAlias("Title")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String title;
|
||||
|
||||
@XStreamAlias("Description")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String description;
|
||||
|
||||
@XStreamAlias("Url")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String url;
|
||||
|
||||
@XStreamAlias("EventKey")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String eventKey;
|
||||
|
||||
@XStreamAlias("Latitude")
|
||||
private Double latitude;
|
||||
|
||||
@XStreamAlias("Longitude")
|
||||
private Double longitude;
|
||||
|
||||
@XStreamAlias("Precision")
|
||||
private Double precision;
|
||||
|
||||
@XStreamAlias("AppType")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String appType;
|
||||
|
||||
@XStreamAlias("ScanCodeInfo")
|
||||
private WxCpXmlMessage.ScanCodeInfo scanCodeInfo = new WxCpXmlMessage.ScanCodeInfo();
|
||||
|
||||
@XStreamAlias("SendPicsInfo")
|
||||
private WxCpXmlMessage.SendPicsInfo sendPicsInfo = new WxCpXmlMessage.SendPicsInfo();
|
||||
|
||||
@XStreamAlias("SendLocationInfo")
|
||||
private WxCpXmlMessage.SendLocationInfo sendLocationInfo = new WxCpXmlMessage.SendLocationInfo();
|
||||
|
||||
@XStreamAlias("ApprovalInfo")
|
||||
private ApprovalInfo approvalInfo = new ApprovalInfo();
|
||||
|
||||
@XStreamAlias("TaskId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
private String taskId;
|
||||
|
||||
@Data
|
||||
@XStreamAlias("ContactSync")
|
||||
public static class ContactSync {
|
||||
@XStreamAlias("AccessToken")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String accessToken;
|
||||
|
||||
@XStreamAlias("ExpiresIn")
|
||||
protected Integer expiresIn;
|
||||
}
|
||||
|
||||
@Data
|
||||
@XStreamAlias("AuthUserInfo")
|
||||
public static class AuthUserInfo {
|
||||
@XStreamAlias("UserId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String userId;
|
||||
}
|
||||
|
||||
@Data
|
||||
@XStreamAlias("BatchJob")
|
||||
public static class BatchJob {
|
||||
@XStreamAlias("JobId")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String JobId;
|
||||
|
||||
@XStreamAlias("JobType")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String jobType;
|
||||
|
||||
@XStreamAlias("ErrCode")
|
||||
@XStreamConverter(value = IntConverter.class)
|
||||
protected Integer errCode;
|
||||
|
||||
@XStreamAlias("ErrMsg")
|
||||
@XStreamConverter(value = XStreamCDataConverter.class)
|
||||
protected String errMsg;
|
||||
}
|
||||
|
||||
@Data
|
||||
@XStreamAlias("ApprovalInfo")
|
||||
public static class ApprovalInfo {
|
||||
@XStreamAlias("ThirdNo")
|
||||
protected Long thirdNo;
|
||||
|
||||
@XStreamAlias("OpenSpName")
|
||||
protected String openSpName;
|
||||
|
||||
@XStreamAlias("OpenTemplateId")
|
||||
protected Integer openTemplateId;
|
||||
|
||||
@XStreamAlias("OpenSpStatus")
|
||||
protected Integer openSpStatus;
|
||||
|
||||
@XStreamAlias("ApplyTime")
|
||||
protected Long applyTime;
|
||||
|
||||
@XStreamAlias("ApplyUserName")
|
||||
protected String applyUserName;
|
||||
|
||||
@XStreamAlias("ApplyUserId")
|
||||
protected Integer applyUserId;
|
||||
|
||||
@XStreamAlias("ApplyUserParty")
|
||||
protected String applyUserParty;
|
||||
|
||||
@XStreamAlias("ApplyUserImage")
|
||||
protected String applyUserImage;
|
||||
|
||||
@XStreamAlias("ApprovalNodes")
|
||||
protected List<ApprovalNode> approvalNodes;
|
||||
|
||||
@XStreamAlias("NotifyNodes")
|
||||
protected List<NotifyNode> notifyNodes;
|
||||
|
||||
@XStreamAlias("approverstep")
|
||||
protected Integer approverstep;
|
||||
|
||||
//自建/第三方应用调用审批流程引擎,状态通知
|
||||
//ref: https://work.weixin.qq.com/api/doc/90001/90143/90376#审批状态通知事件
|
||||
//1.自建/第三方应用调用审批流程引擎发起申请之后,审批状态发生变化时
|
||||
//2.自建/第三方应用调用审批流程引擎发起申请之后,在“审批中”状态,有任意审批人进行审批操作时
|
||||
@Data
|
||||
@XStreamAlias("ApprovalNode")
|
||||
public static class ApprovalNode {
|
||||
@XStreamAlias("NodeStatus")
|
||||
protected Integer nodeStatus;
|
||||
|
||||
@XStreamAlias("NodeAttr")
|
||||
protected Integer nodeAttr;
|
||||
|
||||
@XStreamAlias("NodeType")
|
||||
protected Integer nodeType;
|
||||
|
||||
@XStreamAlias("Items")
|
||||
protected List<Item> items;
|
||||
|
||||
@Data
|
||||
@XStreamAlias("Item")
|
||||
public static class Item {
|
||||
@XStreamAlias("ItemName")
|
||||
protected String itemName;
|
||||
@XStreamAlias("ItemUserId")
|
||||
protected Integer itemUserId;
|
||||
@XStreamAlias("ItemImage")
|
||||
protected String itemImage;
|
||||
@XStreamAlias("ItemStatus")
|
||||
protected Integer itemStatus;
|
||||
@XStreamAlias("ItemSpeech")
|
||||
protected String itemSpeech;
|
||||
@XStreamAlias("ItemOpTime")
|
||||
protected Long itemOpTime;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@XStreamAlias("NotifyNode")
|
||||
public static class NotifyNode {
|
||||
@XStreamAlias("ItemName")
|
||||
protected String itemName;
|
||||
@XStreamAlias("ItemUserId")
|
||||
protected Integer itemUserId;
|
||||
@XStreamAlias("ItemImage")
|
||||
protected String itemImage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static WxCpTpXmlMessage fromXml(String xml) {
|
||||
//修改微信变态的消息内容格式,方便解析
|
||||
//xml = xml.replace("</PicList><PicList>", "");
|
||||
|
||||
@@ -26,67 +26,76 @@ public interface WxCpTpConfigStorage {
|
||||
*/
|
||||
String getApiUrl(String path);
|
||||
|
||||
/**
|
||||
* 第三方应用的suite access token相关
|
||||
*/
|
||||
String getSuiteAccessToken();
|
||||
|
||||
boolean isSuiteAccessTokenExpired();
|
||||
|
||||
/**
|
||||
* 强制将suite access token过期掉.
|
||||
*/
|
||||
//强制将suite access token过期掉.
|
||||
void expireSuiteAccessToken();
|
||||
|
||||
void updateSuiteAccessToken(WxAccessToken suiteAccessToken);
|
||||
void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds);
|
||||
|
||||
void updateSuiteAccessToken(String suiteAccessToken, int expiresIn);
|
||||
|
||||
/**
|
||||
* 第三方应用的suite ticket相关
|
||||
*/
|
||||
String getSuiteTicket();
|
||||
|
||||
boolean isSuiteTicketExpired();
|
||||
|
||||
/**
|
||||
* 强制将suite ticket过期掉.
|
||||
*/
|
||||
//强制将suite ticket过期掉.
|
||||
void expireSuiteTicket();
|
||||
|
||||
/**
|
||||
* 应该是线程安全的.
|
||||
*/
|
||||
//应该是线程安全的
|
||||
void updateSuiteTicket(String suiteTicket, int expiresInSeconds);
|
||||
|
||||
String getCorpId();
|
||||
|
||||
String getCorpSecret();
|
||||
|
||||
/**
|
||||
* 第三方应用的其他配置,来自于企微配置
|
||||
*/
|
||||
String getSuiteId();
|
||||
|
||||
String getSuiteSecret();
|
||||
|
||||
// 第三方应用的token,用来检查应用的签名
|
||||
String getToken();
|
||||
|
||||
//第三方应用的EncodingAESKey,用来检查签名
|
||||
String getAesKey();
|
||||
|
||||
long getExpiresTime();
|
||||
|
||||
String getHttpProxyHost();
|
||||
|
||||
int getHttpProxyPort();
|
||||
|
||||
String getHttpProxyUsername();
|
||||
|
||||
String getHttpProxyPassword();
|
||||
|
||||
File getTmpDirFile();
|
||||
/**
|
||||
* 企微服务商企业ID & 企业secret
|
||||
*/
|
||||
String getCorpId();
|
||||
String getCorpSecret();
|
||||
|
||||
/**
|
||||
* http client builder.
|
||||
*
|
||||
* @return ApacheHttpClientBuilder
|
||||
* 授权企业的access token相关
|
||||
*/
|
||||
String getAccessToken(String authCorpId);
|
||||
boolean isAccessTokenExpired(String authCorpId);
|
||||
void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds);
|
||||
|
||||
/**
|
||||
* 授权企业的js api ticket相关
|
||||
*/
|
||||
String getAuthCorpJsApiTicket(String authCorpId);
|
||||
boolean isAuthCorpJsApiTicketExpired(String authCorpId);
|
||||
void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);
|
||||
|
||||
/**
|
||||
* 授权企业的第三方应用js api ticket相关
|
||||
*/
|
||||
String getAuthSuiteJsApiTicket(String authCorpId);
|
||||
boolean isAuthSuiteJsApiTicketExpired(String authCorpId);
|
||||
void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds);;
|
||||
|
||||
/**
|
||||
* 网络代理相关
|
||||
*/
|
||||
String getHttpProxyHost();
|
||||
int getHttpProxyPort();
|
||||
String getHttpProxyUsername();
|
||||
String getHttpProxyPassword();
|
||||
ApacheHttpClientBuilder getApacheHttpClientBuilder();
|
||||
|
||||
/**
|
||||
* 是否自动刷新token
|
||||
* @return .
|
||||
*/
|
||||
boolean autoRefreshToken();
|
||||
|
||||
// 毫无相关性的代码
|
||||
@Deprecated
|
||||
File getTmpDirFile();
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化.
|
||||
@@ -24,25 +26,34 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
|
||||
private volatile String token;
|
||||
private volatile String suiteAccessToken;
|
||||
private volatile long suiteAccessTokenExpiresTime;
|
||||
private volatile String aesKey;
|
||||
private volatile long expiresTime;
|
||||
|
||||
private volatile String suiteTicket;
|
||||
private volatile long suiteTicketExpiresTime;
|
||||
private volatile String oauth2redirectUri;
|
||||
|
||||
private volatile Map<String, String> authCorpAccessTokenMap = new HashMap<>();
|
||||
private volatile Map<String, Long> authCorpAccessTokenExpireTimeMap = new HashMap<>();
|
||||
|
||||
private volatile Map<String, String> authCorpJsApiTicketMap = new HashMap<>();
|
||||
private volatile Map<String, Long> authCorpJsApiTicketExpireTimeMap = new HashMap<>();
|
||||
|
||||
private volatile Map<String, String> authSuiteJsApiTicketMap = new HashMap<>();
|
||||
private volatile Map<String, Long> authSuiteJsApiTicketExpireTimeMap = new HashMap<>();
|
||||
|
||||
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;
|
||||
@@ -67,12 +78,12 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
|
||||
@Override
|
||||
public boolean isSuiteAccessTokenExpired() {
|
||||
return System.currentTimeMillis() > this.expiresTime;
|
||||
return System.currentTimeMillis() > this.suiteAccessTokenExpiresTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireSuiteAccessToken() {
|
||||
this.expiresTime = 0;
|
||||
this.suiteAccessTokenExpiresTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,25 +94,12 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
@Override
|
||||
public synchronized void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) {
|
||||
this.suiteAccessToken = suiteAccessToken;
|
||||
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
|
||||
this.suiteAccessTokenExpiresTime = 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;
|
||||
@Deprecated
|
||||
public void setSuiteAccessTokenExpiresTime(long suiteAccessTokenExpiresTime) {
|
||||
this.suiteAccessTokenExpiresTime = suiteAccessTokenExpiresTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,23 +107,16 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
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 void expireSuiteTicket() {
|
||||
this.suiteTicketExpiresTime = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void updateSuiteTicket(String suiteTicket, int expiresInSeconds) {
|
||||
this.suiteTicket = suiteTicket;
|
||||
@@ -133,9 +124,20 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
this.suiteTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireSuiteTicket() {
|
||||
this.suiteTicketExpiresTime = 0;
|
||||
|
||||
@Deprecated
|
||||
public void setSuiteTicket(String suiteTicket) {
|
||||
this.suiteTicket = suiteTicket;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public long getSuiteTicketExpiresTime() {
|
||||
return this.suiteTicketExpiresTime;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSuiteTicketExpiresTime(long suiteTicketExpiresTime) {
|
||||
this.suiteTicketExpiresTime = suiteTicketExpiresTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,6 +145,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
return this.suiteId;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSuiteId(String corpId) {
|
||||
this.suiteId = corpId;
|
||||
}
|
||||
@@ -152,6 +155,7 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
return this.suiteSecret;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSuiteSecret(String corpSecret) {
|
||||
this.suiteSecret = corpSecret;
|
||||
}
|
||||
@@ -161,28 +165,109 @@ public class WxCpTpDefaultConfigImpl implements WxCpTpConfigStorage, Serializabl
|
||||
return this.token;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setAesKey(String aesKey) {
|
||||
this.aesKey = aesKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCorpId() {
|
||||
return this.corpId;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setCorpId(String corpId) {
|
||||
this.corpId = corpId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCorpSecret() {
|
||||
return this.corpSecret;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setCorpSecret(String corpSecret) {
|
||||
this.corpSecret = corpSecret;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getAccessToken(String authCorpId) {
|
||||
return authCorpAccessTokenMap.get(authCorpId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessTokenExpired(String authCorpId) {
|
||||
return System.currentTimeMillis() > authCorpAccessTokenExpireTimeMap.get(authCorpId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
|
||||
authCorpAccessTokenMap.put(authCorpId, accessToken);
|
||||
// 预留200秒的时间
|
||||
authCorpAccessTokenExpireTimeMap.put(authCorpId, System.currentTimeMillis() + (expiredInSeconds - 200) * 1000L);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getAuthCorpJsApiTicket(String authCorpId) {
|
||||
return this.authCorpJsApiTicketMap.get(authCorpId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
|
||||
Long t = this.authCorpJsApiTicketExpireTimeMap.get(authCorpId);
|
||||
if (t == null) {
|
||||
return System.currentTimeMillis() > t;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
|
||||
// 应该根据不同的授权企业做区分
|
||||
authCorpJsApiTicketMap.put(authCorpId, jsApiTicket);
|
||||
// 预留200秒的时间
|
||||
authCorpJsApiTicketExpireTimeMap.put(authCorpId, System.currentTimeMillis() + (expiredInSeconds - 200) * 1000L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthSuiteJsApiTicket(String authCorpId) {
|
||||
return authSuiteJsApiTicketMap.get(authCorpId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
|
||||
Long t = authSuiteJsApiTicketExpireTimeMap.get(authCorpId);
|
||||
if (t == null) {
|
||||
return System.currentTimeMillis() > t;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
|
||||
// 应该根据不同的授权企业做区分
|
||||
authSuiteJsApiTicketMap.put(authCorpId, jsApiTicket);
|
||||
// 预留200秒的时间
|
||||
authSuiteJsApiTicketExpireTimeMap.put(authCorpId, System.currentTimeMillis() + (expiredInSeconds - 200) * 1000L);
|
||||
}
|
||||
|
||||
public void setOauth2redirectUri(String oauth2redirectUri) {
|
||||
this.oauth2redirectUri = oauth2redirectUri;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package me.chanjar.weixin.cp.config.impl;
|
||||
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import me.chanjar.weixin.common.bean.WxAccessToken;
|
||||
import me.chanjar.weixin.common.redis.WxRedisOps;
|
||||
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;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 企业微信各种固定、授权配置的Redisson存储实现
|
||||
*/
|
||||
@Builder
|
||||
public class WxCpTpRedissonConfigImpl implements WxCpTpConfigStorage, Serializable {
|
||||
|
||||
@NonNull
|
||||
private final WxRedisOps wxRedisOps;
|
||||
|
||||
//redis里面key的统一前缀
|
||||
private final String keyPrefix = "";
|
||||
|
||||
private final String suiteAccessTokenKey = ":suiteAccessTokenKey:";
|
||||
|
||||
private final String suiteTicketKey = ":suiteTicketKey:";
|
||||
|
||||
private final String accessTokenKey = ":accessTokenKey:";
|
||||
|
||||
private final String authCorpJsApiTicketKey = ":authCorpJsApiTicketKey:";
|
||||
|
||||
private final String authSuiteJsApiTicketKey = ":authSuiteJsApiTicketKey:";
|
||||
|
||||
private volatile String baseApiUrl;
|
||||
private volatile String httpProxyHost;
|
||||
private volatile int httpProxyPort;
|
||||
private volatile String httpProxyUsername;
|
||||
private volatile String httpProxyPassword;
|
||||
private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
|
||||
private volatile File tmpDirFile;
|
||||
|
||||
/**
|
||||
* 第三方应用的其他配置,来自于企微配置
|
||||
*/
|
||||
private volatile String suiteId;
|
||||
private volatile String suiteSecret;
|
||||
// 第三方应用的token,用来检查应用的签名
|
||||
private volatile String token;
|
||||
//第三方应用的EncodingAESKey,用来检查签名
|
||||
private volatile String aesKey;
|
||||
|
||||
/**
|
||||
* 企微服务商企业ID & 企业secret,来自于企微配置
|
||||
*/
|
||||
private volatile String corpId;
|
||||
private volatile String corpSecret;
|
||||
|
||||
@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; }
|
||||
|
||||
|
||||
/**
|
||||
* 第三方应用的suite access token相关
|
||||
*/
|
||||
@Override
|
||||
public String getSuiteAccessToken() {
|
||||
return wxRedisOps.getValue(keyWithPrefix(suiteAccessTokenKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuiteAccessTokenExpired() {
|
||||
//remain time to live in seconds, or key not exist
|
||||
return wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteAccessTokenKey)) == -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireSuiteAccessToken() {
|
||||
wxRedisOps.expire(keyWithPrefix(suiteAccessTokenKey), 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSuiteAccessToken(WxAccessToken suiteAccessToken) {
|
||||
updateSuiteAccessToken(suiteAccessToken.getAccessToken(), suiteAccessToken.getExpiresIn());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSuiteAccessToken(String suiteAccessToken, int expiresInSeconds) {
|
||||
wxRedisOps.setValue(keyWithPrefix(suiteAccessTokenKey), suiteAccessToken, expiresInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方应用的suite ticket相关
|
||||
*/
|
||||
@Override
|
||||
public String getSuiteTicket() {
|
||||
return wxRedisOps.getValue(keyWithPrefix(suiteTicketKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuiteTicketExpired() {
|
||||
//remain time to live in seconds, or key not exist
|
||||
return wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == 0L || wxRedisOps.getExpire(keyWithPrefix(suiteTicketKey)) == -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireSuiteTicket() {
|
||||
wxRedisOps.expire(keyWithPrefix(suiteTicketKey), 0, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSuiteTicket(String suiteTicket, int expiresInSeconds) {
|
||||
wxRedisOps.setValue(keyWithPrefix(suiteTicketKey), suiteTicket, expiresInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 第三方应用的其他配置,来自于企微配置
|
||||
*/
|
||||
@Override
|
||||
public String getSuiteId() {
|
||||
return suiteId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteSecret() {
|
||||
return suiteSecret;
|
||||
}
|
||||
|
||||
// 第三方应用的token,用来检查应用的签名
|
||||
@Override
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
//第三方应用的EncodingAESKey,用来检查签名
|
||||
@Override
|
||||
public String getAesKey() {
|
||||
return aesKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 企微服务商企业ID & 企业secret, 来自于企微配置
|
||||
*/
|
||||
@Override
|
||||
public String getCorpId() {
|
||||
return corpId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCorpSecret() {
|
||||
return corpSecret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 授权企业的access token相关
|
||||
*/
|
||||
@Override
|
||||
public String getAccessToken(String authCorpId) {
|
||||
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + accessTokenKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessTokenExpired(String authCorpId) {
|
||||
//没有设置或者TTL为0,都是过期
|
||||
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == 0L
|
||||
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + accessTokenKey) == -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAccessToken(String authCorpId, String accessToken, int expiredInSeconds) {
|
||||
wxRedisOps.setValue(keyWithPrefix(authCorpId) + accessTokenKey, accessToken, expiredInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 授权企业的js api ticket相关
|
||||
*/
|
||||
@Override
|
||||
public String getAuthCorpJsApiTicket(String authCorpId) {
|
||||
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthCorpJsApiTicketExpired(String authCorpId) {
|
||||
//没有设置或TTL为0,都是过期
|
||||
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == 0L
|
||||
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey) == -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAuthCorpJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
|
||||
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authCorpJsApiTicketKey, jsApiTicket, expiredInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 授权企业的第三方应用js api ticket相关
|
||||
*/
|
||||
@Override
|
||||
public String getAuthSuiteJsApiTicket(String authCorpId) {
|
||||
return wxRedisOps.getValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthSuiteJsApiTicketExpired(String authCorpId) {
|
||||
//没有设置或者TTL为0,都是过期
|
||||
return wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == 0L
|
||||
|| wxRedisOps.getExpire(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey) == -2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAuthSuiteJsApiTicket(String authCorpId, String jsApiTicket, int expiredInSeconds) {
|
||||
wxRedisOps.setValue(keyWithPrefix(authCorpId) + authSuiteJsApiTicketKey, jsApiTicket, expiredInSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 网络代理相关
|
||||
*/
|
||||
@Override
|
||||
public String getHttpProxyHost() {
|
||||
return this.httpProxyHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHttpProxyPort() {
|
||||
return this.httpProxyPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpProxyUsername() {
|
||||
return this.httpProxyUsername;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHttpProxyPassword() {
|
||||
return this.httpProxyPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getTmpDirFile() {
|
||||
return tmpDirFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
|
||||
return this.apacheHttpClientBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean autoRefreshToken() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
//TODO:
|
||||
return WxCpGsonBuilder.create().toJson(this);
|
||||
}
|
||||
|
||||
private String keyWithPrefix(String key) {
|
||||
return keyPrefix + key;
|
||||
}
|
||||
}
|
||||
@@ -140,6 +140,8 @@ public final class WxCpApiPathConsts {
|
||||
public static final String GET_PROVIDER_TOKEN = "/cgi-bin/service/get_provider_token";
|
||||
public static final String GET_PREAUTH_CODE = "/cgi-bin/service/get_pre_auth_code";
|
||||
public static final String GET_AUTH_INFO = "/cgi-bin/service/get_auth_info";
|
||||
public static final String GET_AUTH_CORP_JSAPI_TICKET = "/cgi-bin/get_jsapi_ticket";
|
||||
public static final String GET_SUITE_JSAPI_TICKET = "/cgi-bin/ticket/get";
|
||||
}
|
||||
|
||||
@UtilityClass
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.chanjar.weixin.cp.constant;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
public class WxCpTpConsts {
|
||||
|
||||
|
||||
@UtilityClass
|
||||
public static class InfoType {
|
||||
/**
|
||||
* 推送更新suite_ticket
|
||||
*/
|
||||
public static final String SUITE_TICKET = "suite_ticket";
|
||||
|
||||
/**
|
||||
* 从企业微信应用市场发起授权时,授权成功通知
|
||||
*/
|
||||
public static final String CREATE_AUTH = "create_auth";
|
||||
|
||||
/**
|
||||
* 从企业微信应用市场发起授权时,变更授权通知
|
||||
*/
|
||||
public static final String CHANGE_AUTH = "change_auth";
|
||||
|
||||
/**
|
||||
* 从企业微信应用市场发起授权时,取消授权通知
|
||||
*/
|
||||
public static final String CANCEL_AUTH = "cancel_auth";
|
||||
|
||||
/**
|
||||
* 通讯录变更通知
|
||||
*/
|
||||
public static final String CHANGE_CONTACT = "change_contact";
|
||||
|
||||
/**
|
||||
* 用户进行企业微信的注册,注册完成回调通知
|
||||
*/
|
||||
public static final String REGISTER_CORP = "register_corp";
|
||||
|
||||
/**
|
||||
* 异步任务回调通知
|
||||
*/
|
||||
public static final String BATCH_JOB_RESULT = "batch_job_result";
|
||||
|
||||
/**
|
||||
* 外部联系人变更通知
|
||||
*/
|
||||
public static final String CHANGE_EXTERNAL_CONTACT = "change_external_contact";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package me.chanjar.weixin.cp.tp.message;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package me.chanjar.weixin.cp.tp.message;
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.chanjar.weixin.cp.tp.message;
|
||||
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
|
||||
|
||||
/**
|
||||
@@ -15,6 +16,6 @@ public interface WxCpTpMessageMatcher {
|
||||
* @param message the message
|
||||
* @return the boolean
|
||||
*/
|
||||
boolean match(WxCpXmlMessage message);
|
||||
boolean match(WxCpTpXmlMessage message);
|
||||
|
||||
}
|
||||
|
||||
@@ -10,9 +10,7 @@ import me.chanjar.weixin.common.session.InternalSessionManager;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.common.util.LogExceptionHandler;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
|
||||
import me.chanjar.weixin.cp.message.WxCpMessageRouterRule;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -25,20 +23,22 @@ import java.util.concurrent.*;
|
||||
/**
|
||||
* <pre>
|
||||
* 微信消息路由器,通过代码化的配置,把来自微信的消息交给handler处理
|
||||
* 和WxCpMessageRouter的rule相比,多了infoType和changeType维度的匹配
|
||||
*
|
||||
* 说明:
|
||||
* 1. 配置路由规则时要按照从细到粗的原则,否则可能消息可能会被提前处理
|
||||
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxCpMessageRouterRule#next()}
|
||||
* 3. 规则的结束必须用{@link WxCpMessageRouterRule#end()}或者{@link WxCpMessageRouterRule#next()},否则不会生效
|
||||
* 2. 默认情况下消息只会被处理一次,除非使用 {@link WxCpTpMessageRouterRule#next()}
|
||||
* 3. 规则的结束必须用{@link WxCpTpMessageRouterRule#end()}或者{@link WxCpTpMessageRouterRule#next()},否则不会生效
|
||||
*
|
||||
* 使用方法:
|
||||
* WxCpMessageRouter router = new WxCpMessageRouter();
|
||||
* WxCpTpMessageRouter router = new WxCpTpMessageRouter();
|
||||
* router
|
||||
* .rule()
|
||||
* .msgType("MSG_TYPE").event("EVENT").eventKey("EVENT_KEY").content("CONTENT")
|
||||
* .interceptor(interceptor, ...).handler(handler, ...)
|
||||
* .end()
|
||||
* .rule()
|
||||
* .infoType("INFO_TYPE").changeType("CHANGE_TYPE")
|
||||
* // 另外一个匹配规则
|
||||
* .end()
|
||||
* ;
|
||||
@@ -55,7 +55,7 @@ public class WxCpTpMessageRouter {
|
||||
private static final int DEFAULT_THREAD_POOL_SIZE = 100;
|
||||
private final List<WxCpTpMessageRouterRule> rules = new ArrayList<>();
|
||||
|
||||
private final WxCpTpService wxCpService;
|
||||
private final WxCpTpService wxCpTpService;
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
@@ -68,13 +68,13 @@ public class WxCpTpMessageRouter {
|
||||
/**
|
||||
* 构造方法.
|
||||
*/
|
||||
public WxCpTpMessageRouter(WxCpTpService wxCpService) {
|
||||
this.wxCpService = wxCpService;
|
||||
public WxCpTpMessageRouter(WxCpTpService wxCpTpService) {
|
||||
this.wxCpTpService = wxCpTpService;
|
||||
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("WxCpTpMessageRouter-pool-%d").build();
|
||||
this.executorService = new ThreadPoolExecutor(DEFAULT_THREAD_POOL_SIZE, DEFAULT_THREAD_POOL_SIZE,
|
||||
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), namedThreadFactory);
|
||||
this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
|
||||
this.sessionManager = wxCpService.getSessionManager();
|
||||
this.sessionManager = wxCpTpService.getSessionManager();
|
||||
this.exceptionHandler = new LogExceptionHandler();
|
||||
}
|
||||
|
||||
@@ -160,11 +160,11 @@ public class WxCpTpMessageRouter {
|
||||
if (rule.isAsync()) {
|
||||
futures.add(
|
||||
this.executorService.submit(() -> {
|
||||
rule.service(wxMessage, context, WxCpTpMessageRouter.this.wxCpService, WxCpTpMessageRouter.this.sessionManager, WxCpTpMessageRouter.this.exceptionHandler);
|
||||
rule.service(wxMessage, context, WxCpTpMessageRouter.this.wxCpTpService, WxCpTpMessageRouter.this.sessionManager, WxCpTpMessageRouter.this.exceptionHandler);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
res = rule.service(wxMessage, context, this.wxCpService, this.sessionManager, this.exceptionHandler);
|
||||
res = rule.service(wxMessage, context, this.wxCpTpService, this.sessionManager, this.exceptionHandler);
|
||||
// 在同步操作结束,session访问结束
|
||||
log.debug("End session access: async=false, sessionId={}", wxMessage.getSuiteId());
|
||||
sessionEndAccess(wxMessage);
|
||||
@@ -200,17 +200,30 @@ public class WxCpTpMessageRouter {
|
||||
|
||||
private boolean isMsgDuplicated(WxCpTpXmlMessage wxMessage) {
|
||||
StringBuilder messageId = new StringBuilder();
|
||||
if (StringUtils.isNotEmpty(wxMessage.getSuiteId())) {
|
||||
messageId.append("-").append(wxMessage.getSuiteId());
|
||||
}
|
||||
if (wxMessage.getInfoType() != null) {
|
||||
messageId.append(wxMessage.getInfoType())
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getSuiteId()))
|
||||
.append("-").append(wxMessage.getTimeStamp())
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getAuthCorpId()))
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getUserID()))
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getChangeType()))
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getServiceCorpId()));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(wxMessage.getInfoType())) {
|
||||
messageId.append("-").append(wxMessage.getInfoType());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(wxMessage.getTimeStamp())) {
|
||||
messageId.append("-").append(wxMessage.getTimeStamp());
|
||||
}
|
||||
if (wxMessage.getMsgType() != null) {
|
||||
if (wxMessage.getMsgId() != null) {
|
||||
messageId.append(wxMessage.getMsgId())
|
||||
.append("-").append(wxMessage.getCreateTime())
|
||||
.append("-").append(wxMessage.getFromUserName());
|
||||
}
|
||||
else {
|
||||
messageId.append(wxMessage.getMsgType())
|
||||
.append("-").append(wxMessage.getCreateTime())
|
||||
.append("-").append(wxMessage.getFromUserName())
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getEvent()))
|
||||
.append("-").append(StringUtils.trimToEmpty(wxMessage.getEventKey()));
|
||||
}
|
||||
}
|
||||
|
||||
return this.messageDuplicateChecker.isDuplicate(messageId.toString());
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
|
||||
import me.chanjar.weixin.cp.message.WxCpMessageMatcher;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* The type Wx cp message router rule.
|
||||
@@ -22,15 +23,34 @@ public class WxCpTpMessageRouterRule {
|
||||
|
||||
private boolean async = true;
|
||||
|
||||
private WxCpMessageMatcher matcher;
|
||||
private String fromUser;
|
||||
|
||||
private String msgType;
|
||||
|
||||
private String event;
|
||||
|
||||
private String eventKey;
|
||||
|
||||
private String eventKeyRegex;
|
||||
|
||||
private String content;
|
||||
|
||||
private String rContent;
|
||||
|
||||
private WxCpTpMessageMatcher matcher;
|
||||
|
||||
private boolean reEnter = false;
|
||||
|
||||
private Integer agentId;
|
||||
|
||||
private String infoType;
|
||||
|
||||
private String changeType;
|
||||
|
||||
private List<WxCpTpMessageHandler> handlers = new ArrayList<>();
|
||||
|
||||
private List<WxCpTpMessageInterceptor> interceptors = new ArrayList<>();
|
||||
private String suiteId;
|
||||
private String infoType;
|
||||
private String authCode;
|
||||
private String suiteTicket;
|
||||
|
||||
@@ -64,13 +84,24 @@ public class WxCpTpMessageRouterRule {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果changeType等于这个type,符合rule的条件之一
|
||||
* @param changeType
|
||||
* @return
|
||||
*/
|
||||
public WxCpTpMessageRouterRule changeType(String changeType) {
|
||||
this.changeType = changeType;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 如果消息匹配某个matcher,用在用户需要自定义更复杂的匹配规则的时候
|
||||
*
|
||||
* @param matcher the matcher
|
||||
* @return the wx cp message router rule
|
||||
*/
|
||||
public WxCpTpMessageRouterRule matcher(WxCpMessageMatcher matcher) {
|
||||
public WxCpTpMessageRouterRule matcher(WxCpTpMessageMatcher matcher) {
|
||||
this.matcher = matcher;
|
||||
return this;
|
||||
}
|
||||
@@ -154,13 +185,30 @@ public class WxCpTpMessageRouterRule {
|
||||
protected boolean test(WxCpTpXmlMessage wxMessage) {
|
||||
return
|
||||
(this.suiteId == null || this.suiteId.equals(wxMessage.getSuiteId()))
|
||||
&&
|
||||
(this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
|
||||
&&
|
||||
(this.agentId == null || this.agentId.equals(wxMessage.getAgentID()))
|
||||
&&
|
||||
(this.msgType == null || this.msgType.equalsIgnoreCase(wxMessage.getMsgType()))
|
||||
&&
|
||||
(this.infoType == null || this.infoType.equals(wxMessage.getInfoType()))
|
||||
&&
|
||||
(this.suiteTicket == null || this.suiteTicket.equalsIgnoreCase(wxMessage.getSuiteTicket()))
|
||||
&&
|
||||
(this.authCode == null || this.authCode.equalsIgnoreCase(wxMessage.getAuthCode()))
|
||||
;
|
||||
(this.eventKeyRegex == null || Pattern.matches(this.eventKeyRegex, StringUtils.trimToEmpty(wxMessage.getEventKey())))
|
||||
&&
|
||||
(this.content == null || this.content.equals(StringUtils.trimToNull(wxMessage.getContent())))
|
||||
&&
|
||||
(this.rContent == null || Pattern.matches(this.rContent, StringUtils.trimToEmpty(wxMessage.getContent())))
|
||||
&&
|
||||
(this.infoType == null || this.infoType.equals(wxMessage.getInfoType()))
|
||||
&&
|
||||
(this.changeType == null || this.changeType.equals(wxMessage.getChangeType()))
|
||||
&&
|
||||
(this.matcher == null || this.matcher.match(wxMessage))
|
||||
&&
|
||||
(this.authCode == null || this.authCode.equalsIgnoreCase(wxMessage.getAuthCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -73,12 +73,35 @@ public interface WxCpTpService {
|
||||
* 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
|
||||
* </pre>
|
||||
*
|
||||
* @Deprecated 由于无法主动刷新,所以这个接口实际已经没有意义,需要在接收企业微信的主动推送后,保存这个ticket
|
||||
* @see #setSuiteTicket(String)
|
||||
*
|
||||
* @param forceRefresh 强制刷新
|
||||
* @return the suite ticket
|
||||
* @throws WxErrorException the wx error exception
|
||||
*/
|
||||
@Deprecated
|
||||
String getSuiteTicket(boolean forceRefresh) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 保存企业微信定时推送的suite_ticket,(每10分钟)
|
||||
* 详情请见:https://work.weixin.qq.com/api/doc#90001/90143/90628
|
||||
* </pre>
|
||||
*
|
||||
* @param suiteTicket
|
||||
* @throws WxErrorException
|
||||
*/
|
||||
void setSuiteTicket(String suiteTicket) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取应用的 jsapi ticket
|
||||
*
|
||||
* @param authCorpId 授权企业的cropId
|
||||
* @return jsapi ticket
|
||||
*/
|
||||
String getSuiteJsApiTicket(String authCorpId) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 小程序登录凭证校验
|
||||
*
|
||||
@@ -144,6 +167,14 @@ public interface WxCpTpService {
|
||||
*/
|
||||
WxCpTpAuthInfo getAuthInfo(String authCorpId, String permanentCode) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 获取授权企业的 jsapi ticket
|
||||
*
|
||||
* @param authCorpId 授权企业的cropId
|
||||
* @return jsapi ticket
|
||||
*/
|
||||
String getAuthCorpJsApiTicket(String authCorpId) throws WxErrorException;
|
||||
|
||||
/**
|
||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
|
||||
*
|
||||
@@ -209,8 +240,10 @@ public interface WxCpTpService {
|
||||
/**
|
||||
* 获取WxMpConfigStorage 对象.
|
||||
*
|
||||
* @Deprecated storage应该在service内部使用,提供这个接口,容易破坏这个封装
|
||||
* @return WxMpConfigStorage wx cp tp config storage
|
||||
*/
|
||||
@Deprecated
|
||||
WxCpTpConfigStorage getWxCpTpConfigStorage();
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,10 +45,16 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
|
||||
*/
|
||||
protected final Object globalSuiteAccessTokenRefreshLock = new Object();
|
||||
|
||||
|
||||
/**
|
||||
* 全局刷新suite ticket的锁
|
||||
*/
|
||||
protected final Object globalSuiteTicketRefreshLock = new Object();
|
||||
|
||||
/**
|
||||
* 全局的是否正在刷新jsapi_ticket的锁.
|
||||
*/
|
||||
protected final Object globalSuiteTicketRefreshLock = new Object();
|
||||
protected final Object globalJsApiTicketRefreshLock = new Object();
|
||||
|
||||
protected WxCpTpConfigStorage configStorage;
|
||||
|
||||
@@ -79,16 +85,6 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
|
||||
|
||||
@Override
|
||||
public String getSuiteTicket() throws WxErrorException {
|
||||
return getSuiteTicket(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteTicket(boolean forceRefresh) throws WxErrorException {
|
||||
// suite ticket由微信服务器推送,不能强制刷新
|
||||
// if (forceRefresh) {
|
||||
// this.configStorage.expireSuiteTicket();
|
||||
// }
|
||||
|
||||
if (this.configStorage.isSuiteTicketExpired()) {
|
||||
// 本地suite ticket 不存在或者过期
|
||||
WxError wxError = WxError.fromJson("{\"errcode\":40085, \"errmsg\":\"invaild suite ticket\"}", WxType.CP);
|
||||
@@ -97,6 +93,68 @@ public abstract class BaseWxCpTpServiceImpl<H, P> implements WxCpTpService, Requ
|
||||
return this.configStorage.getSuiteTicket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteTicket(boolean forceRefresh) throws WxErrorException {
|
||||
// suite ticket由微信服务器推送,不能强制刷新
|
||||
// if (forceRefresh) {
|
||||
// this.configStorage.expireSuiteTicket();
|
||||
// }
|
||||
return getSuiteTicket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSuiteTicket(String suiteTicket) throws WxErrorException {
|
||||
synchronized (globalSuiteTicketRefreshLock) {
|
||||
this.configStorage.updateSuiteTicket(suiteTicket, 10 * 60);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuiteJsApiTicket(String authCorpId) throws WxErrorException {
|
||||
if (this.configStorage.isSuiteAccessTokenExpired()) {
|
||||
|
||||
String resp = get(configStorage.getApiUrl(GET_SUITE_JSAPI_TICKET),
|
||||
"type=agent_config&access_token=" + this.configStorage.getAccessToken(authCorpId));
|
||||
|
||||
JsonObject jsonObject = GsonParser.parse(resp);
|
||||
if (jsonObject.get("errcode").getAsInt() == 0) {
|
||||
String jsApiTicket = jsonObject.get("ticket").getAsString();
|
||||
int expiredInSeconds = jsonObject.get("expires_in").getAsInt();
|
||||
synchronized (globalJsApiTicketRefreshLock) {
|
||||
configStorage.updateAuthSuiteJsApiTicket(authCorpId, jsApiTicket, expiredInSeconds);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new WxErrorException(WxError.fromJson(resp));
|
||||
}
|
||||
}
|
||||
|
||||
return configStorage.getSuiteAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthCorpJsApiTicket(String authCorpId) throws WxErrorException {
|
||||
if (this.configStorage.isSuiteAccessTokenExpired()) {
|
||||
|
||||
String resp = get(configStorage.getApiUrl(GET_AUTH_CORP_JSAPI_TICKET),
|
||||
"access_token=" + this.configStorage.getAccessToken(authCorpId));
|
||||
|
||||
JsonObject jsonObject = GsonParser.parse(resp);
|
||||
if (jsonObject.get("errcode").getAsInt() == 0) {
|
||||
String jsApiTicket = jsonObject.get("ticket").getAsString();
|
||||
int expiredInSeconds = jsonObject.get("expires_in").getAsInt();
|
||||
|
||||
synchronized (globalJsApiTicketRefreshLock) {
|
||||
configStorage.updateAuthCorpJsApiTicket(authCorpId, jsApiTicket, expiredInSeconds);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new WxErrorException(WxError.fromJson(resp));
|
||||
}
|
||||
}
|
||||
|
||||
return configStorage.getSuiteAccessToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxCpMaJsCode2SessionResult jsCode2Session(String jsCode) throws WxErrorException {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package me.chanjar.weixin.cp.api;
|
||||
|
||||
import me.chanjar.weixin.common.error.WxErrorException;
|
||||
import me.chanjar.weixin.common.session.WxSessionManager;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpTpXmlMessage;
|
||||
import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage;
|
||||
import me.chanjar.weixin.cp.tp.message.WxCpTpMessageHandler;
|
||||
import me.chanjar.weixin.cp.tp.message.WxCpTpMessageRouter;
|
||||
import me.chanjar.weixin.cp.tp.service.WxCpTpService;
|
||||
import me.chanjar.weixin.cp.tp.service.impl.WxCpTpServiceImpl;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.AssertJUnit.assertEquals;
|
||||
import static org.testng.AssertJUnit.assertNull;
|
||||
|
||||
public class WxCpTpMessageRouterTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testMessageRouter() {
|
||||
WxCpTpService service = new WxCpTpServiceImpl();
|
||||
WxCpTpMessageRouter router = new WxCpTpMessageRouter(service);
|
||||
|
||||
String xml = "<xml>\n" +
|
||||
" <SuiteId><![CDATA[ww4asffe99e54cxxxx]]></SuiteId>\n" +
|
||||
" <AuthCorpId><![CDATA[wxf8b4f85f3a79xxxx]]></AuthCorpId>\n" +
|
||||
" <InfoType><![CDATA[change_contact]]></InfoType>\n" +
|
||||
" <TimeStamp>1403610513</TimeStamp>\n" +
|
||||
" <ChangeType><![CDATA[update_tag]]></ChangeType>\n" +
|
||||
" <TagId>1</TagId>\n" +
|
||||
" <AddUserItems><![CDATA[zhangsan,lisi]]></AddUserItems>\n" +
|
||||
" <DelUserItems><![CDATA[zhangsan1,lisi1]]></DelUserItems>\n" +
|
||||
" <AddPartyItems><![CDATA[1,2]]></AddPartyItems>\n" +
|
||||
" <DelPartyItems><![CDATA[3,4]]></DelPartyItems>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
|
||||
router.rule().infoType("change_contact").changeType("update_tag").handler(new WxCpTpMessageHandler() {
|
||||
@Override
|
||||
public WxCpXmlOutMessage handle(WxCpTpXmlMessage wxMessage, Map<String, Object> context, WxCpTpService wxCpService, WxSessionManager sessionManager) throws WxErrorException {
|
||||
System.out.println("handler enter");
|
||||
assertNotNull(wxCpService);
|
||||
return null;
|
||||
}
|
||||
}).end();
|
||||
|
||||
assertNull(router.route(wxXmlMessage));
|
||||
|
||||
|
||||
System.out.println("over");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package me.chanjar.weixin.cp.bean.message;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
public class WxCpTpXmlMessageTest {
|
||||
|
||||
@Test
|
||||
public void testUserNotifyXML() {
|
||||
String xml = "<xml>\n" +
|
||||
" <SuiteId><![CDATA[ww4asffe99e54c0f4c]]></SuiteId>\n" +
|
||||
" <AuthCorpId><![CDATA[wxf8b4f85f3axxxxxx]]></AuthCorpId>\n" +
|
||||
" <InfoType><![CDATA[change_contact]]></InfoType>\n" +
|
||||
" <TimeStamp>1403610513</TimeStamp>\n" +
|
||||
" <ChangeType><![CDATA[create_user]]></ChangeType>\n" +
|
||||
" <UserID><![CDATA[zhangsan]]></UserID>\n" +
|
||||
" <Name><![CDATA[张三]]></Name>\n" +
|
||||
" <Department><![CDATA[1,2,3]]></Department>\n" +
|
||||
" <MainDepartment>1</MainDepartment>\n" +
|
||||
" <IsLeaderInDept><![CDATA[1,0,0]]></IsLeaderInDept>\n" +
|
||||
" <Mobile><![CDATA[11111111111]]></Mobile>\n" +
|
||||
" <Position><![CDATA[产品经理]]></Position>\n" +
|
||||
" <Gender>1</Gender>\n" +
|
||||
" <Email><![CDATA[zhangsan@xxx.com]]></Email>\n" +
|
||||
" <Avatar><![CDATA[http://wx.qlogo.cn/mmopen/ajNVdqHZLLA3WJ6DSZUfiakYe37PKnQhBIeOQBO4czqrnZDS79FH5Wm5m4X69TBicnHFlhiafvDwklOpZeXYQQ2icg/0]]></Avatar>\n" +
|
||||
" <Alias><![CDATA[zhangsan]]></Alias>\n" +
|
||||
" <Telephone><![CDATA[020-111111]]></Telephone>\n" +
|
||||
" <ExtAttr>\n" +
|
||||
" <Item>\n" +
|
||||
" <Name><![CDATA[爱好]]></Name>\n" +
|
||||
" <Type>0</Type>\n" +
|
||||
" <Text>\n" +
|
||||
" <Value><![CDATA[旅游]]></Value>\n" +
|
||||
" </Text>\n" +
|
||||
" </Item>\n" +
|
||||
" <Item>\n" +
|
||||
" <Name><![CDATA[卡号]]></Name>\n" +
|
||||
" <Type>1</Type>\n" +
|
||||
" <Web>\n" +
|
||||
" <Title><![CDATA[企业微信]]></Title>\n" +
|
||||
" <Url><![CDATA[https://work.weixin.qq.com]]></Url>\n" +
|
||||
" </Web>\n" +
|
||||
" </Item>\n" +
|
||||
" </ExtAttr>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
assertEquals(wxXmlMessage.getSuiteId(), "ww4asffe99e54c0f4c");
|
||||
assertEquals(wxXmlMessage.getPosition(), "产品经理");
|
||||
assertEquals(wxXmlMessage.getGender(), Integer.valueOf(1));
|
||||
assertEquals(wxXmlMessage.getTelephone(), "020-111111");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRegisterCorp() {
|
||||
String xml = "<xml>\n" +
|
||||
" <ServiceCorpId><![CDATA[wwddddccc7775555aab]]></ServiceCorpId>\n" +
|
||||
" <InfoType><![CDATA[register_corp]]></InfoType>\n" +
|
||||
" <TimeStamp>1502682173</TimeStamp>\n" +
|
||||
" <RegisterCode><![CDATA[pIKi3wRPNWCGF-pyP-YU5KWjDDD]]></RegisterCode>\n" +
|
||||
" <AuthCorpId><![CDATA[wwddddccc7775555aaa]]></AuthCorpId>\n" +
|
||||
" <ContactSync>\n" +
|
||||
" <AccessToken><![CDATA[accesstoken000001]]></AccessToken>\n" +
|
||||
" <ExpiresIn>1800</ExpiresIn>\n" +
|
||||
" </ContactSync>\n" +
|
||||
" <AuthUserInfo>\n" +
|
||||
" <UserId><![CDATA[zhangshan]]></UserId>\n" +
|
||||
" </AuthUserInfo>\n" +
|
||||
" <State><![CDATA[state1]]></State>\n" +
|
||||
" <TemplateId><![CDATA[tpl1test]]></TemplateId>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
assertEquals(wxXmlMessage.getServiceCorpId(), "wwddddccc7775555aab");
|
||||
assertEquals(wxXmlMessage.getInfoType(), "register_corp");
|
||||
assertEquals(wxXmlMessage.getRegisterCode(), "pIKi3wRPNWCGF-pyP-YU5KWjDDD");
|
||||
assertNotNull(wxXmlMessage.getContactSync());
|
||||
assertEquals(wxXmlMessage.getContactSync().getAccessToken(), "accesstoken000001");
|
||||
assertEquals(wxXmlMessage.getContactSync().getExpiresIn(), Integer.valueOf(1800));
|
||||
assertNotNull(wxXmlMessage.getAuthUserInfo());
|
||||
assertEquals(wxXmlMessage.getAuthUserInfo().getUserId(), "zhangshan");
|
||||
assertEquals(wxXmlMessage.getTemplateId(), "tpl1test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tagNotifyTest() {
|
||||
String xml = "<xml>\n" +
|
||||
" <SuiteId><![CDATA[ww4asffe99e54cxxxx]]></SuiteId>\n" +
|
||||
" <AuthCorpId><![CDATA[wxf8b4f85f3a79xxxx]]></AuthCorpId>\n" +
|
||||
" <InfoType><![CDATA[change_contact]]></InfoType>\n" +
|
||||
" <TimeStamp>1403610513</TimeStamp>\n" +
|
||||
" <ChangeType><![CDATA[update_tag]]></ChangeType>\n" +
|
||||
" <TagId>1</TagId>\n" +
|
||||
" <AddUserItems><![CDATA[zhangsan,lisi]]></AddUserItems>\n" +
|
||||
" <DelUserItems><![CDATA[zhangsan1,lisi1]]></DelUserItems>\n" +
|
||||
" <AddPartyItems><![CDATA[1,2]]></AddPartyItems>\n" +
|
||||
" <DelPartyItems><![CDATA[3,4]]></DelPartyItems>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
|
||||
assertEquals(wxXmlMessage.getTagId(), Integer.valueOf(1));
|
||||
assertNotNull(wxXmlMessage.getAddUserItems());
|
||||
assertEquals(wxXmlMessage.getAddUserItems()[0], "zhangsan");
|
||||
assertEquals(wxXmlMessage.getAddUserItems()[1], "lisi");
|
||||
|
||||
assertNotNull(wxXmlMessage.getDelUserItems());
|
||||
assertNotNull(wxXmlMessage.getDelUserItems()[0], "zhangsan1");
|
||||
assertNotNull(wxXmlMessage.getDelUserItems()[0], "lisi1");
|
||||
|
||||
assertNotNull(wxXmlMessage.getAddPartyItems());
|
||||
assertEquals(wxXmlMessage.getAddPartyItems()[0], Integer.valueOf(1));
|
||||
assertEquals(wxXmlMessage.getAddPartyItems()[1], Integer.valueOf(2));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void enterAppTest() {
|
||||
String xml = "<xml><ToUserName><![CDATA[toUser]]></ToUserName>\n" +
|
||||
"<FromUserName><![CDATA[FromUser]]></FromUserName>\n" +
|
||||
"<CreateTime>1408091189</CreateTime>\n" +
|
||||
"<MsgType><![CDATA[event]]></MsgType>\n" +
|
||||
"<Event><![CDATA[enter_agent]]></Event>\n" +
|
||||
"<EventKey><![CDATA[]]></EventKey>\n" +
|
||||
"<AgentID>1</AgentID>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
assertEquals(wxXmlMessage.getToUserName(), "toUser");
|
||||
assertEquals(wxXmlMessage.getFromUserName(), "FromUser");
|
||||
assertEquals(wxXmlMessage.getCreateTime(), Long.valueOf(1408091189));
|
||||
assertEquals(wxXmlMessage.getEvent(), "enter_agent");
|
||||
assertEquals(wxXmlMessage.getEventKey(), "");
|
||||
assertEquals(wxXmlMessage.getAgentID(), Integer.valueOf(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textMessageTest() {
|
||||
String xml = "<xml>\n" +
|
||||
" <ToUserName><![CDATA[toUser]]></ToUserName>\n" +
|
||||
" <FromUserName><![CDATA[fromUser]]></FromUserName> \n" +
|
||||
" <CreateTime>1348831860</CreateTime>\n" +
|
||||
" <MsgType><![CDATA[text]]></MsgType>\n" +
|
||||
" <Content><![CDATA[this is a test]]></Content>\n" +
|
||||
" <MsgId>1234567890123456</MsgId>\n" +
|
||||
" <AgentID>1</AgentID>\n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
assertEquals(wxXmlMessage.getToUserName(), "toUser");
|
||||
assertEquals(wxXmlMessage.getFromUserName(), "fromUser");
|
||||
assertEquals(wxXmlMessage.getCreateTime(), Long.valueOf(1348831860));
|
||||
assertEquals(wxXmlMessage.getMsgType(), "text");
|
||||
assertEquals(wxXmlMessage.getMsgId(), "1234567890123456");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ApprovalInfoTest() {
|
||||
String xml = "<xml>\n" +
|
||||
" <ToUserName>wwddddccc7775555aaa</ToUserName> \n" +
|
||||
" <FromUserName>sys</FromUserName> \n" +
|
||||
" <CreateTime>1527838022</CreateTime> \n" +
|
||||
" <MsgType>event</MsgType> \n" +
|
||||
" <Event>open_approval_change</Event>\n" +
|
||||
" <AgentID>1</AgentID>\n" +
|
||||
" <ApprovalInfo> \n" +
|
||||
" <ThirdNo>201806010001</ThirdNo> \n" +
|
||||
" <OpenSpName>付款</OpenSpName> \n" +
|
||||
" <OpenTemplateId>1234567890</OpenTemplateId> \n" +
|
||||
" <OpenSpStatus>1</OpenSpStatus> \n" +
|
||||
" <ApplyTime>1527837645</ApplyTime> \n" +
|
||||
" <ApplyUserName>xiaoming</ApplyUserName> \n" +
|
||||
" <ApplyUserId>1</ApplyUserId> \n" +
|
||||
" <ApplyUserParty>产品部</ApplyUserParty> \n" +
|
||||
" <ApplyUserImage>http://www.qq.com/xxx.png</ApplyUserImage> \n" +
|
||||
" <ApprovalNodes> \n" +
|
||||
" <ApprovalNode> \n" +
|
||||
" <NodeStatus>1</NodeStatus> \n" +
|
||||
" <NodeAttr>1</NodeAttr> \n" +
|
||||
" <NodeType>1</NodeType> \n" +
|
||||
" <Items> \n" +
|
||||
" <Item> \n" +
|
||||
" <ItemName>xiaohong</ItemName> \n" +
|
||||
" <ItemUserId>2</ItemUserId> \n" +
|
||||
" <ItemImage>http://www.qq.com/xxx.png</ItemImage> \n" +
|
||||
" <ItemStatus>1</ItemStatus> \n" +
|
||||
" <ItemSpeech></ItemSpeech> \n" +
|
||||
" <ItemOpTime>0</ItemOpTime> \n" +
|
||||
" </Item> \n" +
|
||||
" </Items> \n" +
|
||||
" </ApprovalNode> \n" +
|
||||
" </ApprovalNodes> \n" +
|
||||
" <NotifyNodes> \n" +
|
||||
" <NotifyNode> \n" +
|
||||
" <ItemName>xiaogang</ItemName> \n" +
|
||||
" <ItemUserId>3</ItemUserId> \n" +
|
||||
" <ItemImage>http://www.qq.com/xxx.png</ItemImage> \n" +
|
||||
" </NotifyNode> \n" +
|
||||
" </NotifyNodes> \n" +
|
||||
" <approverstep>0</approverstep> \n" +
|
||||
" </ApprovalInfo> \n" +
|
||||
"</xml>";
|
||||
|
||||
WxCpTpXmlMessage wxXmlMessage = WxCpTpXmlMessage.fromXml(xml);
|
||||
assertEquals(wxXmlMessage.getToUserName(), "wwddddccc7775555aaa");
|
||||
assertEquals(wxXmlMessage.getFromUserName(), "sys");
|
||||
assertEquals(wxXmlMessage.getCreateTime(), Long.valueOf(1527838022));
|
||||
assertEquals(wxXmlMessage.getEvent(), "open_approval_change");
|
||||
|
||||
assertNotNull(wxXmlMessage.getApprovalInfo());
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getThirdNo(), Long.valueOf(201806010001L));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getOpenSpName(), "付款");
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getThirdNo(), Long.valueOf(201806010001L));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApplyTime(), Long.valueOf(1527837645));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApplyUserName(), "xiaoming");
|
||||
|
||||
assertNotNull(wxXmlMessage.getApprovalInfo().getApprovalNodes());
|
||||
assertNotNull(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getNodeAttr(), Integer.valueOf(1));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getNodeType(), Integer.valueOf(1));
|
||||
|
||||
assertNotNull(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems());
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems().get(0).getItemName(), "xiaohong");
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems().get(0).getItemOpTime(), Long.valueOf(0));
|
||||
|
||||
assertNotNull(wxXmlMessage.getApprovalInfo().getNotifyNodes().get(0));
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getNotifyNodes().get(0).getItemImage(), "http://www.qq.com/xxx.png");
|
||||
assertEquals(wxXmlMessage.getApprovalInfo().getNotifyNodes().get(0).getItemUserId(), Integer.valueOf(3));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user