🎨 #2773【企业微信】优化会话存档,增加会话存档的多企业支持

This commit is contained in:
0katekate0 2022-08-12 11:42:43 +08:00 committed by GitHub
parent 53d332190c
commit fe49e5af11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 91 additions and 28 deletions

View File

@ -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;
/**
* 获取媒体文件
@ -58,6 +60,7 @@ 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
@ -65,7 +68,7 @@ public interface WxCpMsgAuditService {
* @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;
/**
* 获取会话内容存档开启成员列表

View File

@ -76,11 +76,11 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
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);
}
@ -88,7 +88,7 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
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);
}
@ -97,20 +97,21 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
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 会话存档私钥不能为空
@ -124,7 +125,6 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
/**
* 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice在使用完slice中数据后还需要调用FreeSlice释放
*/
long sdk = Finance.SingletonSDK();
long msg = Finance.NewSlice();
/**
@ -135,7 +135,7 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
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);
}
@ -148,12 +148,12 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
}
@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
@ -166,13 +166,13 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
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);
}

View File

@ -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中的粉丝数.
*/

View File

@ -12,7 +12,8 @@ import java.util.List;
/**
* 聊天记录数据内容.
*
* @author Wang_Wong
* @author <a href="https://github.com/0katekate0">Wang_Wong</a>
* @date 2022-01-17
*/
@Data
public class WxCpChatDatas implements Serializable {
@ -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;

View File

@ -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;
@ -61,7 +60,7 @@ public class WxCpCryptUtil extends WxCryptUtil {
* @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(" ", "");
@ -87,7 +86,7 @@ public class WxCpCryptUtil extends WxCryptUtil {
* @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(" ", "");

View File

@ -3,10 +3,13 @@ package me.chanjar.weixin.cp.api;
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;
@ -18,7 +21,8 @@ import java.util.Date;
* 企业微信直播测试类.
* 官方文档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 {
@ -36,6 +40,31 @@ public class WxCpLivingTest {
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));
/**
* 测试创建直播
*/

View File

@ -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;
@ -114,7 +115,23 @@ public class WxCpMsgAuditTest {
* <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重入拉取
@ -145,13 +162,13 @@ public class WxCpMsgAuditTest {
// 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());
/**
@ -239,13 +256,15 @@ public class WxCpMsgAuditTest {
* 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());
}