me.chanjar.weixin.cp.api.impl.WxCpMsgAuditServiceImpl Maven / Gradle / Ivy
package me.chanjar.weixin.cp.api.impl;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.tencent.wework.Finance;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpMsgAuditService;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.cp.bean.msgaudit.*;
import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.MsgAudit.*;
/**
* 会话内容存档接口实现类.
*
* @author Wang_Wong created on 2022-01-17
*/
@Slf4j
@RequiredArgsConstructor
public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
private final WxCpService cpService;
@Override
public WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, String passwd,
@NonNull long timeout) throws Exception {
String configPath = cpService.getWxCpConfigStorage().getMsgAuditLibPath();
if (StringUtils.isEmpty(configPath)) {
throw new WxErrorException("请配置会话存档sdk文件的路径,不要配错了!!");
}
/**
* 完整的文件库路径:
*
* /www/osfile/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll,
* libWeWorkFinanceSdk_Java.so
*/
// 替换斜杠
String replacePath = configPath.replace("\\", "/");
// 获取最后一个斜杠的下标,用作分割路径
int lastIndex = replacePath.lastIndexOf("/") + 1;
// 获取完整路径的前缀路径
String prefixPath = replacePath.substring(0, lastIndex);
// 获取后缀的所有文件,目的遍历所有文件
String suffixFiles = replacePath.substring(lastIndex);
// 包含so文件
String[] libFiles = suffixFiles.split(",");
if (libFiles.length <= 0) {
throw new WxErrorException("请仔细配置会话存档文件路径!!");
}
List libList = Arrays.asList(libFiles);
// 判断windows系统会话存档sdk中dll的加载,因为会互相依赖所以是有顺序的,否则会导致无法加载sdk #2598
List osLib = new LinkedList();
List fileLib = new ArrayList();
libList.stream().forEach(s -> {
if (s.contains("lib")) {
osLib.add(s);
} else {
fileLib.add(s);
}
});
osLib.addAll(fileLib);
Finance.loadingLibraries(osLib, prefixPath);
long sdk = Finance.NewSdk();
//因为会话存档单独有个secret,优先使用会话存档的secret
String msgAuditSecret = cpService.getWxCpConfigStorage().getMsgAuditSecret();
if (StringUtils.isEmpty(msgAuditSecret)) {
msgAuditSecret = cpService.getWxCpConfigStorage().getCorpSecret();
}
long ret = Finance.Init(sdk, cpService.getWxCpConfigStorage().getCorpId(), msgAuditSecret);
if (ret != 0) {
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.DestroySdk(sdk);
throw new WxErrorException("getchatdata err ret " + ret);
}
// 拉取会话存档
String content = Finance.GetContentFromSlice(slice);
Finance.FreeSlice(slice);
WxCpChatDatas chatDatas = WxCpChatDatas.fromJson(content);
if (chatDatas.getErrCode().intValue() != 0) {
Finance.DestroySdk(sdk);
throw new WxErrorException(chatDatas.toJson());
}
chatDatas.setSdk(sdk);
return chatDatas;
}
@Override
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);
}
/**
* Decrypt chat data string.
*
* @param sdk the sdk
* @param chatData the chat data
* @param pkcs1 the pkcs 1
* @return the string
* @throws Exception the exception
*/
public String decryptChatData(long sdk, WxCpChatDatas.WxCpChatData chatData, Integer pkcs1) throws Exception {
/**
* 企业获取的会话内容,使用企业自行配置的消息加密公钥进行加密,企业可用自行保存的私钥解开会话内容数据。
* msgAuditPriKey 会话存档私钥不能为空
*/
String priKey = cpService.getWxCpConfigStorage().getMsgAuditPriKey();
if (StringUtils.isEmpty(priKey)) {
throw new WxErrorException("请配置会话存档私钥【msgAuditPriKey】");
}
String decryptByPriKey = WxCpCryptUtil.decryptPriKey(chatData.getEncryptRandomKey(), priKey, pkcs1);
/**
* 每次使用DecryptData解密会话存档前需要调用NewSlice获取一个slice,在使用完slice中数据后,还需要调用FreeSlice释放。
*/
long msg = Finance.NewSlice();
/**
* 解密会话存档内容
* sdk不会要求用户传入rsa私钥,保证用户会话存档数据只有自己能够解密。
* 此处需要用户先用rsa私钥解密encrypt_random_key后,作为encrypt_key参数传入sdk来解密encrypt_chat_msg获取会话存档明文。
*/
int ret = Finance.DecryptData(sdk, decryptByPriKey, chatData.getEncryptChatMsg(), msg);
if (ret != 0) {
Finance.FreeSlice(msg);
Finance.DestroySdk(sdk);
throw new WxErrorException("msg err ret " + ret);
}
/**
* 明文
*/
String plainText = Finance.GetContentFromSlice(msg);
Finance.FreeSlice(msg);
return plainText;
}
@Override
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 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。
* 3、indexbuf一般格式如右侧所示,”Range:bytes=524288-1048575“:表示这次拉取的是从524288到1048575的分片。单个文件首次拉取填写的indexbuf
* 为空字符串,拉取后续分片时直接填入上次返回的indexbuf即可。
*/
File targetFile = new File(targetFilePath);
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
this.getMediaFile(sdk, sdkfileid, proxy, passwd, timeout, i -> {
try {
// 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。
FileOutputStream outputStream = new FileOutputStream(targetFile, true);
outputStream.write(i);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
});
}
@Override
public void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull Consumer action) throws WxErrorException {
/**
* 1、媒体文件每次拉取的最大size为512k,因此超过512k的文件需要分片拉取。
* 2、若该文件未拉取完整,sdk的IsMediaDataFinish接口会返回0,同时通过GetOutIndexBuf接口返回下次拉取需要传入GetMediaData的indexbuf。
* 3、indexbuf一般格式如右侧所示,”Range:bytes=524288-1048575“:表示这次拉取的是从524288到1048575的分片。单个文件首次拉取填写的indexbuf为空字符串,拉取后续分片时直接填入上次返回的indexbuf即可。
*/
String indexbuf = "";
int ret, data_len = 0;
log.debug("正在分片拉取媒体文件 sdkFileId为{}", sdkfileid);
while (true) {
long mediaData = Finance.NewMediaData();
ret = Finance.GetMediaData(sdk, indexbuf, sdkfileid, proxy, passwd, timeout, mediaData);
if (ret != 0) {
Finance.FreeMediaData(mediaData);
Finance.DestroySdk(sdk);
throw new WxErrorException("getmediadata err ret " + ret);
}
data_len += Finance.GetDataLen(mediaData);
log.debug("正在分片拉取媒体文件 len:{}, data_len:{}, is_finish:{} \n", Finance.GetIndexLen(mediaData), data_len, Finance.IsMediaDataFinish(mediaData));
try {
// 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。
action.accept(Finance.GetData(mediaData));
} catch (Exception e) {
e.printStackTrace();
}
if (Finance.IsMediaDataFinish(mediaData) == 1) {
// 已经拉取完成最后一个分片
Finance.FreeMediaData(mediaData);
break;
} else {
// 获取下次拉取需要使用的indexbuf
indexbuf = Finance.GetOutIndexBuf(mediaData);
Finance.FreeMediaData(mediaData);
}
}
}
@Override
public List getPermitUserList(Integer type) throws WxErrorException {
final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_PERMIT_USER_LIST);
JsonObject jsonObject = new JsonObject();
if (type != null) {
jsonObject.addProperty("type", type);
}
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
return WxCpGsonBuilder.create().fromJson(GsonParser.parse(responseContent).getAsJsonArray("ids"),
new TypeToken>() {
}.getType());
}
@Override
public WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorException {
final String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(GET_GROUP_CHAT);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("roomid", roomid);
String responseContent = this.cpService.post(apiUrl, jsonObject.toString());
return WxCpGroupChat.fromJson(responseContent);
}
@Override
public WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException {
String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(CHECK_SINGLE_AGREE);
String responseContent = this.cpService.post(apiUrl, checkAgreeRequest.toJson());
return WxCpAgreeInfo.fromJson(responseContent);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy