背景
企业微信会话存档(Message Archive)是金融、保险、政务等强合规场景下常见需求。本文介绍如何通过企业微信官方 SDK 接入会话存档 API,实现聊天记录拉取、解密、存储与检索的完整流程。
参考文档:企业微信会话存档开发者文档
前置条件
- 已开通企业微信会话存档功能(需管理员在企业微信后台开启,部分版本收费)
- 获取
corpid(企业ID)和corpsecret(应用密钥) - 下载企业微信官方会话存档 SDK(支持 C/C++、Java、Go 等语言的封装库)
- 安装 SDK 依赖的动态库(Linux:
libFinanceAI.so;Windows:FinanceAI.dll)
⚠️ 注意:企业微信会话存档 SDK 目前官方提供 C 版本,第三方有封装的 Java/Python wrapper,但稳定性需评估,生产环境建议使用官方 C SDK 或 Go 封装。
核心 API 概览
1. 获取会话记录
// C SDK 核心函数
int GetChatData(
const char* corpid,
const char* corpsecret,
uint64_t seq, // 拉取起始seq,首次填0
uint32_t limit, // 单次拉取数量,最大1000
const char* proxy, // 代理设置,无代理填""
const char* passwd, // 代理密码,无代理填""
uint32_t timeout, // 超时时间(秒)
WXFinanceAI_SLICE** pp_slice // 输出参数:返回的数据切片
);
Java 封装示例(使用第三方 wework-sdk):
import com.weixin.finance.sdk.Finance;
public class ArchiveService {
private static final String CORP_ID = "your_corpid";
private static final String SECRET = "your_corpsecret";
public List<ChatRecord> fetchRecords(long fromSeq, int limit) throws Exception {
// 初始化SDK(仅需一次)
Finance finance = new Finance();
int ret = finance.Init(CORP_ID, SECRET);
if (ret != 0) {
throw new RuntimeException("SDK初始化失败,错误码:" + ret);
}
// 拉取数据
Slice slice = new Slice();
int fetchRet = finance.GetChatData(fromSeq, limit, "", "", 5, slice);
if (fetchRet != 0) {
throw new RuntimeException("拉取会话记录失败,错误码:" + fetchRet);
}
String content = finance.GetContentFromSlice(slice);
finance.FreeSlice(slice);
// 解析JSON
return parseRecords(content);
}
}
2. 消息解密
会话存档的消息内容是加密的,需使用企业微信提供的消息密钥解密:
public String decryptMessage(String encryptRandomKey, String encryptMsg, String priKeyPath)
throws Exception {
Finance finance = new Finance();
// 加载企业私钥(RSA 2048位)
String priKey = new String(Files.readAllBytes(Paths.get(priKeyPath)));
// 解密随机密钥
DecryptData decryptData = new DecryptData();
int ret = finance.DecryptData(priKey, encryptRandomKey, encryptMsg, decryptData);
if (ret != 0) {
throw new RuntimeException("消息解密失败,错误码:" + ret);
}
return finance.GetContentFromDecryptData(decryptData);
}
3. 媒体文件下载(图片、语音、视频)
public void downloadMediaFile(String sdkFileid, String savePath) throws Exception {
Finance finance = new Finance();
int indexBuf = 0;
StringBuilder fileContent = new StringBuilder();
// 分片下载
while (true) {
MediaData mediaData = new MediaData();
int ret = finance.GetMediaData(
indexBuf, // 分片序号
sdkFileid, // 文件ID(来自消息体)
"", "", // 代理设置
5, // 超时(秒)
mediaData
);
if (ret != 0) break;
// 写入文件
byte[] data = finance.GetDataFromMediaData(mediaData);
appendToFile(savePath, data);
if (finance.IsMediaDataFinish(mediaData) == 1) break;
indexBuf++;
finance.FreeMediaData(mediaData);
}
}
数据库存储设计
建议以下表结构存储解密后的会话记录:
CREATE TABLE chat_records (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
seq BIGINT NOT NULL COMMENT '消息序号,用于增量拉取',
msgid VARCHAR(64) NOT NULL UNIQUE COMMENT '消息唯一ID',
action VARCHAR(20) COMMENT '消息动作:send/recall/switch',
from_user VARCHAR(64) COMMENT '发送方企业微信用户ID',
tolist JSON COMMENT '接收方列表',
roomid VARCHAR(64) COMMENT '群聊ID,单聊为空',
msgtime BIGINT COMMENT '消息时间戳(毫秒)',
msgtype VARCHAR(20) COMMENT '消息类型:text/image/voice/file等',
content LONGTEXT COMMENT '解密后的消息内容(JSON格式)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_seq (seq),
INDEX idx_from_user (from_user),
INDEX idx_msgtime (msgtime)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
增量同步机制
会话存档拉取是基于 seq(序号)的增量机制,类似消息队列的 offset:
@Scheduled(fixedDelay = 60000) // 每分钟同步一次
public void syncChatRecords() {
// 从数据库取上次同步的最大seq
long lastSeq = archiveMapper.getMaxSeq();
boolean hasMore = true;
while (hasMore) {
List<ChatRecord> records = archiveService.fetchRecords(lastSeq, 100);
if (records.isEmpty()) {
hasMore = false;
break;
}
// 批量插入
archiveMapper.batchInsert(records);
// 更新seq
lastSeq = records.stream()
.mapToLong(ChatRecord::getSeq)
.max()
.orElse(lastSeq);
hasMore = records.size() == 100; // 满100条说明还有更多
}
log.info("会话存档同步完成,当前seq: {}", lastSeq);
}
搜索与检索接口
存档数据入库后,可以提供合规查询接口:
@GetMapping("/api/archive/search")
public PageResult<ChatRecord> searchRecords(
@RequestParam String userId, // 查询指定员工
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "50") int size
) {
// 权限校验:仅管理员和合规人员可查询
checkPermission();
return archiveMapper.searchRecords(userId, startTime, endTime, keyword, page, size);
}
注意事项
合规提示(重要):
- 开启会话存档后,系统会向员工和客户展示存档提示,这是企业微信强制要求,不能关闭
- 存档数据属于企业内部合规数据,访问接口需做权限控制,避免数据泄露
- 数据保存期限建议参考行业监管要求(金融行业通常要求保留5年以上)
性能建议:
- 单次拉取不超过1000条,大量历史数据导入建议分批次执行
- 媒体文件下载建议异步处理,避免阻塞主流程
- seq 索引务必建立,增量同步依赖此字段
SDK 版本:
- 定期关注企业微信开发者文档的 SDK 更新,接口有时会有安全性修复
- 参考文档:https://developer.work.weixin.qq.com/document/path/91774
常见问题
Q:拉取数据返回空,但确认有聊天记录?
A:检查三点:①会话存档功能是否在管理后台正式开启(不是试用状态);②拉取的 seq 是否超出范围(首次务必从0开始);③密钥和 corpid 是否匹配。
Q:DecryptData 报错 -1?
A:通常是私钥格式问题。企业微信要求 PEM 格式的 RSA 私钥,注意头尾行(-----BEGIN RSA PRIVATE KEY-----)不能丢失。
Q:媒体文件下载到一半中断怎么处理?
A:记录已下载的分片序号,支持断点续传,重新从中断的 indexBuf 继续下载即可。
关于华万通信
上海华万通信科技有限公司是腾讯系企业软件生态服务商,专注为企业提供腾讯会议、企业微信、腾讯电子签及WorkBuddy企业AI等产品的选型、部署与集成服务,帮助企业实现数字化协同升级。