Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【企业微信】增加会话存档多企业支持 #2773

Merged
merged 3 commits into from
Aug 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,24 @@ public interface WxCpMsgAuditService {
/**
* 获取解密的聊天数据Model
*
* @param sdk getChatDatas()获取到的sdk
* @param chatData getChatDatas()获取到的聊天数据
* @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的聊天数据
* @throws Exception
*/
WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;
WxCpChatModel getDecryptData(@NonNull long sdk, @NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;

/**
* 获取解密的聊天数据明文
*
* @param sdk getChatDatas()获取到的sdk
* @param chatData getChatDatas()获取到的聊天数据
* @param pkcs1 使用什么方式进行解密,1代表使用PKCS1进行解密,2代表PKCS8进行解密 ...
* @return 解密后的明文
* @throws Exception
*/
String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;
String getChatPlainText(@NonNull long sdk, @NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception;

/**
* 获取媒体文件
Expand All @@ -58,14 +60,15 @@ public interface WxCpMsgAuditService {
* 根据上面返回的文件类型,拼接好存放文件的绝对路径即可。此时绝对路径写入文件流,来达到获取媒体文件的目的。
* 详情可以看官方文档,亦可阅读此接口源码。
*
* @param sdk getChatDatas()获取到的sdk,注意,每次获取的sdk会不一样
* @param sdkfileid 消息体内容中的sdkfileid信息
* @param proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081,如果没有传null
* @param passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123,如果没有传null
* @param timeout 超时时间,分片数据需累加到文件存储。单次最大返回512K字节,如果文件比较大,自行设置长一点,比如timeout=10000
* @param targetFilePath 目标文件绝对路径+实际文件名,比如:/usr/local/file/20220114/474f866b39d10718810d55262af82662.gif
* @throws WxErrorException
*/
void getMediaFile(@NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException;
void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException;

/**
* 获取会话内容存档开启成员列表
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,19 @@ public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, S
osLib.addAll(fileLib);

Finance.loadingLibraries(osLib, prefixPath);
long sdk = Finance.SingletonSDK();
long sdk = Finance.NewSdk();

long ret = Finance.Init(sdk, cpService.getWxCpConfigStorage().getCorpId(), cpService.getWxCpConfigStorage().getCorpSecret());
if (ret != 0) {
Finance.DestroySingletonSDK(sdk);
Finance.DestroySdk(sdk);
throw new WxErrorException("init sdk err ret " + ret);
}

long slice = Finance.NewSlice();
ret = Finance.GetChatData(sdk, seq, limit, proxy, passwd, timeout, slice);
if (ret != 0) {
Finance.FreeSlice(slice);
Finance.DestroySingletonSDK(sdk);
Finance.DestroySdk(sdk);
throw new WxErrorException("getchatdata err ret " + ret);
}

Expand All @@ -97,20 +97,21 @@ public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, S
Finance.FreeSlice(slice);
WxCpChatDatas chatDatas = WxCpChatDatas.fromJson(content);
if (chatDatas.getErrCode().intValue() != 0) {
Finance.DestroySingletonSDK(sdk);
Finance.DestroySdk(sdk);
throw new WxErrorException(chatDatas.toJson());
}

chatDatas.setSdk(sdk);
return chatDatas;
}

@Override
public WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
String plainText = this.decryptChatData(chatData, pkcs1);
public WxCpChatModel getDecryptData(@NonNull long sdk, @NonNull WxCpChatDatas.WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
String plainText = this.decryptChatData(sdk, chatData, pkcs1);
return WxCpChatModel.fromJson(plainText);
}

public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1) throws Exception {
public String decryptChatData(long sdk, WxCpChatDatas.WxCpChatData chatData, Integer pkcs1) throws Exception {
/**
* 企业获取的会话内容,使用企业自行配置的消息加密公钥进行加密,企业可用自行保存的私钥解开会话内容数据。
* msgAuditPriKey 会话存档私钥不能为空
Expand All @@ -124,7 +125,6 @@ public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1
/**
* 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
*/
long sdk = Finance.SingletonSDK();
long msg = Finance.NewSlice();

/**
Expand All @@ -135,7 +135,7 @@ public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1
int ret = Finance.DecryptData(sdk, decryptByPriKey, chatData.getEncryptChatMsg(), msg);
if (ret != 0) {
Finance.FreeSlice(msg);
Finance.DestroySingletonSDK(sdk);
Finance.DestroySdk(sdk);
throw new WxErrorException("msg err ret " + ret);
}

Expand All @@ -148,12 +148,12 @@ public String decryptChatData(WxCpChatDatas.WxCpChatData chatData, Integer pkcs1
}

@Override
public String getChatPlainText(WxCpChatDatas.@NonNull WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
return this.decryptChatData(chatData, pkcs1);
public String getChatPlainText(@NonNull long sdk, WxCpChatDatas.@NonNull WxCpChatData chatData, @NonNull Integer pkcs1) throws Exception {
return this.decryptChatData(sdk, chatData, pkcs1);
}

@Override
public void getMediaFile(@NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException {
public void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull String targetFilePath) throws WxErrorException {
/**
* 1、媒体文件每次拉取的最大size为512k,因此超过512k的文件需要分片拉取。
* 2、若该文件未拉取完整,sdk的IsMediaDataFinish接口会返回0,同时通过GetOutIndexBuf接口返回下次拉取需要传入GetMediaData的indexbuf。
Expand All @@ -166,13 +166,13 @@ public void getMediaFile(@NonNull String sdkfileid, String proxy, String passwd,

String indexbuf = "";
int ret, data_len = 0;
log.debug("正在分片拉取媒体文件 sdkFileId为{}", sdkfileid);
while (true) {
long mediaData = Finance.NewMediaData();
long sdk = Finance.SingletonSDK();
ret = Finance.GetMediaData(sdk, indexbuf, sdkfileid, proxy, passwd, timeout, mediaData);
if (ret != 0) {
Finance.FreeMediaData(mediaData);
Finance.DestroySingletonSDK(sdk);
Finance.DestroySdk(sdk);
throw new WxErrorException("getmediadata err ret " + ret);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,11 +434,20 @@ public class WxCpXmlMessage implements Serializable {
* 1. 群发的结果.
* 2. 通讯录变更事件
* 激活状态:1=已激活 2=已禁用 4=未激活 已激活代表已激活企业微信或已关注微工作台(原企业号).
* 3. 直播回调事件
* 直播状态 ,0:预约中,1:直播中,2:已结束,4:已取消 (已过期状态目前没有回调)
*/
@XStreamAlias("Status")
@XStreamConverter(value = XStreamCDataConverter.class)
private String status;

/**
* 直播ID
*/
@XStreamAlias("LivingId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String livingId;

/**
* group_id下粉丝数;或者openid_list中的粉丝数.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
/**
* 聊天记录数据内容.
*
* @author Wang_Wong
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2022-01-17
*/
@Data
public class WxCpChatDatas implements Serializable {
Expand All @@ -24,6 +25,9 @@ public class WxCpChatDatas implements Serializable {
@SerializedName("errmsg")
private String errMsg;

@SerializedName("sdk")
private long sdk;

@SerializedName("chatdata")
private List<WxCpChatData> chatData;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package me.chanjar.weixin.cp.util.crypto;

import com.google.common.io.BaseEncoding;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.crypto.WxCryptUtil;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
Expand Down Expand Up @@ -61,7 +60,7 @@ public static String decryptPriKey(String encryptRandomKey, String msgAuditPriKe
* @throws Exception
*/
public static String decryptPriKeyByPKCS8(String encryptRandomKey, String msgAuditPriKey) throws Exception {
String privateKey = msgAuditPriKey.replaceAll("\\n", "")
String privateKey = msgAuditPriKey.replaceAll("(\r\n|\r|\n|\n\r)", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll(" ", "");
Expand All @@ -87,7 +86,7 @@ public static String decryptPriKeyByPKCS8(String encryptRandomKey, String msgAud
* @throws Exception
*/
public static String decryptPriKeyByPKCS1(String encryptRandomKey, String msgAuditPriKey) throws Exception {
String privateKey = msgAuditPriKey.replaceAll("\\n", "")
String privateKey = msgAuditPriKey.replaceAll("(\r\n|\r|\n|\n\r)", "")
.replace("-----BEGIN RSA PRIVATE KEY-----", "")
.replace("-----END RSA PRIVATE KEY-----", "")
.replaceAll(" ", "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.XmlUtils;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
import me.chanjar.weixin.cp.bean.living.*;
import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage;
import me.chanjar.weixin.cp.config.WxCpConfigStorage;
import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
import org.eclipse.jetty.util.ajax.JSON;
import org.testng.annotations.Test;

Expand All @@ -18,7 +21,8 @@
* 企业微信直播测试类.
* 官方文档:https://open.work.weixin.qq.com/api/doc/90000/90135/93632
*
* @author Wang_Wong
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2021-12-23
*/
@Slf4j
public class WxCpLivingTest {
Expand All @@ -36,6 +40,31 @@ public void test() throws WxErrorException {
wxCpService = new WxCpServiceImpl();
wxCpService.setWxCpConfigStorage(config);


/**
* 直播回调事件
* 一场完整的直播,会经历 预约直播/开始直播/结束直播 等一系列状态变更。
* 为了让企业实时获取直播的动态,当直播状态变更后,企业微信会将该变更推送到开发者配置的回调URL。
* 只有通过接口创建的预约/立即直播才会回调。
*
* 请注意,只有用企业微信api创建的直播才能收到回调,且调用创建直播接口的应用,要配置好回调url。
*/
String livingXml = "<xml>\n" +
" <ToUserName><![CDATA[toUser]]></ToUserName>\n" +
" <FromUserName><![CDATA[fromUser]]></FromUserName> \n" +
" <CreateTime>1348831860</CreateTime>\n" +
" <MsgType><![CDATA[event]]></MsgType>\n" +
" <Event><![CDATA[living_status_change]]></Event>\n" +
" <LivingId><![CDATA[LivingId]]></LivingId>\n" +
" <Status>1</Status>\n" +
" <AgentID>1</AgentID>\n" +
"</xml>";

final WxCpXmlMessage livingXmlMsg = XStreamTransformer.fromXml(WxCpXmlMessage.class, livingXml);
livingXmlMsg.setAllFieldsMap(XmlUtils.xml2Map(livingXml));
log.info("livingXmlMsg:{}", JSON.toString(livingXmlMsg));


/**
* 测试创建直播
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.chanjar.weixin.cp.api;

import com.google.common.collect.Lists;
import com.tencent.wework.Finance;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.XmlUtils;
import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl;
Expand Down Expand Up @@ -114,7 +115,23 @@ public void test() throws Exception {
* <msgAuditLibPath>D:/WorkSpace/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so</msgAuditLibPath>
* Linux:
* <msgAuditLibPath>/www/osfile/work_msg_storage/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so</msgAuditLibPath>
*/
*
*
* yml配置(支持多个corpId):
* wx:
* cp:
* appConfigs:
* - agentId: 10001 #客户联系
* corpId: xxxxxxxxxxx
* secret: T5fTj1n-sBAT4rKNW5c9IYNfPdXZxxxxxxxxxxx
* token: 2bSNqTcLtxxxxxxxxxxx
* aesKey: AXazu2Xyw44SNY1x8go2phn9p9B2xxxxxxxxxxx
* - agentId: 10002 #会话内容存档
* corpId: xxxxxxxxxxx
* secret: xIpum7Yt4NMXcyxdzcQ2l_46BG4Qxxxxxxxxxxx
* token:
* aesKey:
* /

/**
* 建议放到redis,本次请求获取消息记录开始的seq值。首次访问填写0,非首次使用上次企业微信返回的最大seq。允许从任意seq重入拉取。
Expand Down Expand Up @@ -145,13 +162,13 @@ public void test() throws Exception {
// Integer publickeyVer = chatData.getPublickeyVer();

// 获取明文数据
final String chatPlainText = cpService.getMsgAuditService().getChatPlainText(chatData, 2);
final String chatPlainText = cpService.getMsgAuditService().getChatPlainText(chatDatas.getSdk(), chatData, 2);
final WxCpChatModel wxCpChatModel = WxCpChatModel.fromJson(chatPlainText);
log.info("明文数据为:{}", wxCpChatModel.toJson());

// 获取消息数据
// https://developer.work.weixin.qq.com/document/path/91774
final WxCpChatModel decryptData = cpService.getMsgAuditService().getDecryptData(chatData, 2);
final WxCpChatModel decryptData = cpService.getMsgAuditService().getDecryptData(chatDatas.getSdk(), chatData, 2);
log.info("获取消息数据为:{}", decryptData.toJson());

/**
Expand Down Expand Up @@ -239,13 +256,15 @@ public void test() throws Exception {
* 3、比如可以上传到阿里云oss或者腾讯云cos
*/
String targetPath = path + md5Sum + suffix;
cpService.getMsgAuditService().getMediaFile(sdkFileId, null, null, 1000L, targetPath);
cpService.getMsgAuditService().getMediaFile(chatDatas.getSdk(), sdkFileId, null, null, 1000L, targetPath);

}

}

}
// 注意:
// 当此批次数据拉取完毕后,可以释放此次sdk
log.info("释放sdk {}", chatDatas.getSdk());
Finance.DestroySdk(chatDatas.getSdk());

}

Expand Down