Skip to content

Commit

Permalink
🆕 #1596 【企业微信】新增会话存档相关接口
Browse files Browse the repository at this point in the history
  • Loading branch information
0katekate0 authored and binarywang committed Jan 24, 2022
1 parent b8dd04b commit c0535f8
Show file tree
Hide file tree
Showing 19 changed files with 2,308 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public class WxCpProperties {
* 微信企业号应用 EncodingAESKey
*/
private String aesKey;
/**
* 微信企业号应用 会话存档类库路径
*/
private String msgAuditLibPath;

/**
* 配置存储策略,默认内存
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
package me.chanjar.weixin.common.util.crypto;

import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import com.google.common.base.CharMatcher;
import com.google.common.io.BaseEncoding;
import lombok.AllArgsConstructor;
import lombok.Data;
import me.chanjar.weixin.common.error.WxRuntimeException;
Expand All @@ -22,6 +9,18 @@
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;

/**
* <pre>
* 对公众平台发送给公众账号的消息加解密示例代码.
Expand Down
207 changes: 207 additions & 0 deletions weixin-java-cp/src/main/java/com/tencent/wework/Finance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package com.tencent.wework;

import lombok.extern.slf4j.Slf4j;

/**
* 注意:
* 此类必须配置在com.tencent.wework路径底下,否则会报错:
* java.lang.UnsatisfiedLinkError: com.xxx.Finance.NewSdk()
* <p>
* Q:JAVA版本的sdk报错UnsatisfiedLinkError?
* A:请检查是否修改了sdk的包名。
* <p>
* 官方文档:
* https://developer.work.weixin.qq.com/document/path/91552
*
* @author Wang_Wong
* @date 2022-01-17
*/
@Slf4j
public class Finance {

private static volatile long sdk = -1L;
private static Finance finance = null;
private static final String SO_FILE = "so";
private static final String DLL_FILE = "dll";

public native static long NewSdk();

/**
* 初始化函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] corpid 调用企业的企业id,例如:wwd08c8exxxx5ab44d,可以在企业微信管理端--我的企业--企业信息查看
* @param [in] secret 聊天内容存档的Secret,可以在企业微信管理端--管理工具--聊天内容存档查看
* @return 返回是否初始化成功
* 0 - 成功
* !=0 - 失败
*/
public native static int Init(long sdk, String corpid, String secret);

/**
* 拉取聊天记录函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
* @param [in] limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误
* @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
* @param [out] chatDatas 返回本次拉取消息的数据,slice结构体.内容包括errcode/errmsg,以及每条消息内容。
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
public native static int GetChatData(long sdk, long seq, long limit, String proxy, String passwd, long timeout, long chatData);

/**
* 拉取媒体消息函数
* Return值=0表示该API调用成功
*
* @param [in] sdk NewSdk返回的sdk指针
* @param [in] sdkFileid 从GetChatData返回的聊天消息中,媒体消息包括的sdkfileid
* @param [in] proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081
* @param [in] passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123
* @param [in] indexbuf 媒体消息分片拉取,需要填入每次拉取的索引信息。首次不需要填写,默认拉取512k,后续每次调用只需要将上次调用返回的outindexbuf填入即可。
* @param [out] media_data 返回本次拉取的媒体数据.MediaData结构体.内容包括data(数据内容)/outindexbuf(下次索引)/is_finish(拉取完成标记)
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
*/
public native static int GetMediaData(long sdk, String indexbuf, String sdkField, String proxy, String passwd, long timeout, long mediaData);

/**
* @param [in] encrypt_key, getchatdata返回的encrypt_key
* @param [in] encrypt_msg, getchatdata返回的content
* @param [out] msg, 解密的消息明文
* @return 返回是否调用成功
* 0 - 成功
* !=0 - 失败
* @brief 解析密文
*/
public native static int DecryptData(long sdk, String encrypt_key, String encrypt_msg, long msg);

public native static void DestroySdk(long sdk);

public native static long NewSlice();

/**
* @return
* @brief 释放slice,和NewSlice成对使用
*/
public native static void FreeSlice(long slice);

/**
* @return 内容
* @brief 获取slice内容
*/
public native static String GetContentFromSlice(long slice);

/**
* @return 内容
* @brief 获取slice内容长度
*/
public native static int GetSliceLen(long slice);

public native static long NewMediaData();

public native static void FreeMediaData(long mediaData);

/**
* @return outindex
* @brief 获取mediadata outindex
*/
public native static String GetOutIndexBuf(long mediaData);

/**
* @return data
* @brief 获取mediadata data数据
*/
public native static byte[] GetData(long mediaData);

public native static int GetIndexLen(long mediaData);

public native static int GetDataLen(long mediaData);

/**
* @return 1完成、0未完成
* @brief 判断mediadata是否结束
*/
public native static int IsMediaDataFinish(long mediaData);

/**
* 判断Windows环境
*
* @return
*/
public static boolean isWindows() {
String osName = System.getProperties().getProperty("os.name");
log.info("Loading System Libraries, Current OS Version Is: {}", osName);
return osName.toUpperCase().contains("WINDOWS");
}

/**
* 加载系统类库
*
* @param libFiles 类库配置文件
* @param prefixPath 类库文件的前缀路径
*/
public Finance(String[] libFiles, String prefixPath) {
boolean isWindows = Finance.isWindows();
for (String file : libFiles) {
String suffix = file.substring(file.lastIndexOf(".") + 1);
if (isWindows) {
// 加载dll文件
if (suffix.equalsIgnoreCase(DLL_FILE)) {
System.load(prefixPath + file);
}
} else {
// 加载so文件
if (suffix.equalsIgnoreCase(SO_FILE)) {
System.load(prefixPath + file);
}
}
}

}

/**
* 初始化类库文件
*
* @param libFiles
* @param prefixPath
* @return
*/
public synchronized static Finance loadingLibraries(String[] libFiles, String prefixPath) {
if (finance != null) {
return finance;
}
finance = new Finance(libFiles, prefixPath);
return finance;
}

/**
* 单例sdk
*
* @return
*/
public synchronized static long SingletonSDK() {
if (sdk > 0) {
return sdk;
}
sdk = Finance.NewSdk();
return sdk;
}

/**
* 销毁sdk,保证线程可见性
*
* @return
*/
public synchronized static void DestroySingletonSDK(long destroySDK) {
sdk = 0L;
Finance.DestroySdk(destroySDK);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package me.chanjar.weixin.cp.api;

import lombok.NonNull;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.msgaudit.*;

import java.util.List;

/**
* 会话内容存档接口.
* 官方文档:https://developer.work.weixin.qq.com/document/path/91360
* <p>
* 如需自行实现,亦可调用Finance类库函数,进行实现:
* com.tencent.wework.Finance
*
* @author Wang_Wong
* @date 2022-01-14
*/
public interface WxCpMsgAuditService {

/**
* 拉取聊天记录函数
*
* @param seq 从指定的seq开始拉取消息,注意的是返回的消息从seq+1开始返回,seq为之前接口返回的最大seq值。首次使用请使用seq:0
* @param limit 一次拉取的消息条数,最大值1000条,超过1000条会返回错误
* @param proxy 使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081,如果没有传null
* @param passwd 代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123,如果没有传null
* @param timeout 超时时间,根据实际需要填写
* @return 返回是否调用成功
*/
WxCpChatDatas getChatDatas(long seq, @NonNull long limit, String proxy, String passwd, @NonNull long timeout) throws Exception;

/**
* 获取解密的聊天数据Model
*
* @param chatData getChatDatas()获取到的聊天数据
* @return 解密后的聊天数据
* @throws Exception
*/
WxCpChatModel getDecryptData(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;

/**
* 获取解密的聊天数据明文
*
* @param chatData getChatDatas()获取到的聊天数据
* @return 解密后的明文
* @throws Exception
*/
String getChatPlainText(@NonNull WxCpChatDatas.WxCpChatData chatData) throws Exception;

/**
* 获取媒体文件
* 针对图片、文件等媒体数据,提供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;

/**
* 获取会话内容存档开启成员列表
* 企业可通过此接口,获取企业开启会话内容存档的成员列表
* <p>
* 请求方式:POST(HTTPS)
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/get_permit_user_list?access_token=ACCESS_TOKEN
*
* @param type 拉取对应版本的开启成员列表。1表示办公版;2表示服务版;3表示企业版。非必填,不填写的时候返回全量成员列表。
* @return
* @throws WxErrorException
*/
List<String> getPermitUserList(Integer type) throws WxErrorException;

/**
* 获取会话内容存档内部群信息
* 企业可通过此接口,获取会话内容存档本企业的内部群信息,包括群名称、群主id、公告、群创建时间以及所有群成员的id与加入时间。
* <p>
* 请求方式:POST(HTTPS)
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/groupchat/get?access_token=ACCESS_TOKEN
*
* @param roomid 待查询的群id
* @return
* @throws WxErrorException
*/
WxCpGroupChat getGroupChat(@NonNull String roomid) throws WxErrorException;

/**
* 获取会话同意情况
* 企业可通过下述接口,获取会话中外部成员的同意情况
* <p>
* 单聊请求地址:https://qyapi.weixin.qq.com/cgi-bin/msgaudit/check_single_agree?access_token=ACCESS_TOKEN
* <p>
* 请求方式:POST(HTTPS)
*
* @param checkAgreeRequest 待查询的会话信息
* @return
* @throws WxErrorException
*/
WxCpAgreeInfo checkSingleAgree(@NonNull WxCpCheckAgreeRequest checkAgreeRequest) throws WxErrorException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,13 @@ public interface WxCpService extends WxService {
*/
WxCpLivingService getLivingService();

/**
* 获取会话存档相关接口的服务类对象
*
* @return
*/
WxCpMsgAuditService getMsgAuditService();

/**
* 获取日历相关接口的服务类对象
*
Expand Down
Loading

0 comments on commit c0535f8

Please sign in to comment.