From fe49e5af1180b19ab6a14ae39e0082490c4980b3 Mon Sep 17 00:00:00 2001 From: 0katekate0 <32161300+0katekate0@users.noreply.github.com> Date: Fri, 12 Aug 2022 11:42:43 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20#2773=E3=80=90=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E3=80=91=E4=BC=98=E5=8C=96=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E5=AD=98=E6=A1=A3=E7=9A=84=E5=A4=9A=E4=BC=81=E4=B8=9A=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/cp/api/WxCpMsgAuditService.java | 9 ++++-- .../cp/api/impl/WxCpMsgAuditServiceImpl.java | 28 ++++++++--------- .../cp/bean/message/WxCpXmlMessage.java | 9 ++++++ .../cp/bean/msgaudit/WxCpChatDatas.java | 6 +++- .../weixin/cp/util/crypto/WxCpCryptUtil.java | 5 ++- .../chanjar/weixin/cp/api/WxCpLivingTest.java | 31 ++++++++++++++++++- .../weixin/cp/api/WxCpMsgAuditTest.java | 31 +++++++++++++++---- 7 files changed, 91 insertions(+), 28 deletions(-) diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java index 95f484675f..72f637040f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java @@ -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; /** * 获取会话内容存档开启成员列表 diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java index fa802a1c68..5f670f483e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java @@ -76,11 +76,11 @@ 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); } @@ -88,7 +88,7 @@ public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, S 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 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 会话存档私钥不能为空 @@ -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(); /** @@ -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); } @@ -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。 @@ -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); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java index 68dbc69878..6a81a7b50c 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java @@ -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中的粉丝数. */ diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java index 8359bc087d..212cb8b200 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatDatas.java @@ -12,7 +12,8 @@ /** * 聊天记录数据内容. * - * @author Wang_Wong + * @author Wang_Wong + * @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 chatData; diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java index 35b5946edb..8d6a682569 100755 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/crypto/WxCpCryptUtil.java @@ -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 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(" ", ""); @@ -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(" ", ""); diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java index b67324e711..5ee990f9b7 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpLivingTest.java @@ -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; @@ -18,7 +21,8 @@ * 企业微信直播测试类. * 官方文档:https://open.work.weixin.qq.com/api/doc/90000/90135/93632 * - * @author Wang_Wong + * @author Wang_Wong + * @date 2021-12-23 */ @Slf4j public class WxCpLivingTest { @@ -36,6 +40,31 @@ public void test() throws WxErrorException { wxCpService = new WxCpServiceImpl(); wxCpService.setWxCpConfigStorage(config); + + /** + * 直播回调事件 + * 一场完整的直播,会经历 预约直播/开始直播/结束直播 等一系列状态变更。 + * 为了让企业实时获取直播的动态,当直播状态变更后,企业微信会将该变更推送到开发者配置的回调URL。 + * 只有通过接口创建的预约/立即直播才会回调。 + * + * 请注意,只有用企业微信api创建的直播才能收到回调,且调用创建直播接口的应用,要配置好回调url。 + */ + String livingXml = "\n" + + " \n" + + " \n" + + " 1348831860\n" + + " \n" + + " \n" + + " \n" + + " 1\n" + + " 1\n" + + ""; + + final WxCpXmlMessage livingXmlMsg = XStreamTransformer.fromXml(WxCpXmlMessage.class, livingXml); + livingXmlMsg.setAllFieldsMap(XmlUtils.xml2Map(livingXml)); + log.info("livingXmlMsg:{}", JSON.toString(livingXmlMsg)); + + /** * 测试创建直播 */ diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java index cdd2647c93..7c3c4c9a80 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java @@ -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 void test() throws Exception { * D:/WorkSpace/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so * Linux: * /www/osfile/work_msg_storage/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,libWeWorkFinanceSdk_Java.so - */ + * + * + * 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 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()); /** @@ -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()); }