获取会话记录
会话内容分很多种,如:文本、图像、撤回消息、语音、视频等。所以使用模板方法设计模式实现企业微信会话获取、解密、保存等操作
优化记录:
[2024年08月05日 - v1.1.0]
- 新建 JSON 相关处理的工具类 (JsonUtil.java),提供
读取 JSON 文本转换为 Dto
、根据指定名称的节点读取 JSON 文本中对应的值
、判断是否返回有效的 JSON 文本
等方法以及相应的重载方法- 修改抽象类/模板方法 DecryptBaseMessage 类,添加获取媒体数据的实现
- 按照会话内容类型新建数据表,保存明文消息
- 重构
*ResponseDto.java
等数据Dto
类,优化继承关系,减少子类中重写的字段数量,提高后期的扩展性- 重构模板方法,抽取共通的子类方法,提高扩展性
- 重构子类,提高扩展性
运行环境
SpringBoot2 + Mybatis-Plus
MySQL 8.0+
OpenJDK 1.8
一、准备工作
获取企业微信 SDK : https://developer.work.weixin.qq.com/document/path/91774
这里使用 Windows 的 SDK(sdk_win_v2.0.zip)
- 下载后解压文件
- 打开解压路径:%你的解压路径%\financeWinSdk\java_sdk\WeWorkFinanceSdk
- 将以下的四个 dll 放到
C:\Windows\System32
文件夹内, 注意WeWorkFinanceSdk.dll
的名字,最好是这个名称而且需要和Finance.java
中静态块的名称要对上(不建议改名称,我改了之后出问题了)- libcrypto-1_1-x64.dll
- libcurl-x64.dll
- libssl-1_1-x64.dll
- WeWorkFinanceSdk.dll
- 将
Finance.java
放到项目中- 文件需要放在
com\tencent\wework
包下,不能是其他包,如:com/xxxx/util
等 Finance.java
文件编码格式是 GBK,注意格式转换
- 文件需要放在
如果使用 Linux 环境的 SDK
- 下载后解压文件
- 将
libWeWorkFinanceSdk_Java.so
文件改名为libWeWorkFinanceSdk.so
(改成其他名称出现问题) - 将
libWeWorkFinanceSdk.so
文件移动到/usr/lib
下 - 将
Finance.java
放到项目中- 注意
libWeWorkFinanceSdk.so
的名字,最好是这个名称而且需要和Finance.java
中静态块的名称要对上 - 文件需要放在
com\tencent\wework
包下,不能是其他包,如:com/xxxx/util
等 Finance.java
文件编码格式是GBK
,注意格式转换
- 注意
生成公钥和私钥
- 安装 Open SSL,并在 windows 的环境变量中添加 Open SSL 变量(安装路径的bin目录即可)
- 打开命令提示符窗口,运行以下命令先生成 rsa pkcs1 2048 格式的私钥
openssl genrsa -out private.pem
- 生成私钥后,运行以下命令生成对应的公钥
openssl rsa -in private.pem -pubout -out public.pem
-
打开
public.pem
文件,将公钥内容配置到企业微信的管理后台,这时要注意公钥的版本号
-
保存好私钥文件(
private.pem
)
二、配置文件
根据实际环境添加配置参数
,在 application-dev.yml
文件中添加以下内容
wecom:
corpId:
secret: 会话内容存档的secret
limit: 1000 # 一次拉取的消息条数,最大值1000条,超过1000条会返回错误
timeout: 60 # 超时时间,单位秒
privatePemFilePath: D:\pem\private.pem # 私钥PEM文件存放位置,用于解密会话消息,该私钥文件在上一步生成
publickeyVer: 2 # 公钥版本号,用于筛选数据,如果
fileSavePath:
image: # 图片文件保存路径,如资源需保存在云上,则忽略此项配置
file: # 文件保存路径,如资源需保存在云上,则忽略此项配置
voice: # 语音文件保存路径,如资源需保存在云上,则忽略此项配置
wecom.corpId
:企业IDwecom.secret
:在管理后台获取secretwecom.limit
:一次拉取的消息条数,最大值1000条,超过1000条会返回错误,这个值需要修改的,有时候设定 1000 会导致 JVM Crash (原因:内存泄漏)wecom.timeout
:超时时间,单位秒wecom.privatePemFilePath
:PEM文件(私钥)存放位置,用于解密会话消息wecom.publickeyVer
:公钥版本号,用于筛选数据,使用同一版本的公钥和私钥才能解密数据
wecom.fileSavePath.image
:图片文件保存路径,如资源需保存在云上,则忽略此项配置wecom.fileSavePath.file
:文件保存路径,如资源需保存在云上,则忽略此项配置wecom.fileSavePath.voice
:语音文件保存路径,如资源需保存在云上,则忽略此项配置
pom.xml
导入以下的依赖
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
<!-- 排除 bcprov-jdk16 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk16</artifactId>
<version>1.46</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.64</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.64</version>
</dependency>
- 建议使用
${gson.version}
、${fastjson.version}
、${bcpg.version}
、${bcpkix.version}
、${bcprov.version}
指定依赖版本
三、流程图
四、数据表
encrypted_session_message
: 保存会话内容的密文数据
CREATE TABLE `encrypted_session_message` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`seq` bigint DEFAULT NULL COMMENT '消息的seq值,标识消息的序号。再次拉取需要带上上次回包中最大的seq。',
`msg_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '消息id,消息的唯一标识,企业可以使用此字段进行消息去重。String类型。msgid以_external结尾的消息,表明该消息是一条外部消息。msgid以_updown_stream结尾的消息,表明该消息是一条上下游消息。',
`publickey_ver` int DEFAULT NULL COMMENT '加密此条消息使用的公钥版本号',
`encrypt_random_key` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '使用publickey_ver指定版本的公钥进行非对称加密后base64加密的内容,需要业务方先base64 decode处理后,再使用指定版本的私钥进行解密,得出内容。',
`encrypt_chat_msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '消息密文。需要业务方使用将encrypt_random_key解密得到的内容,与encrypt_chat_msg,传入sdk接口DecryptData,得到消息明文。',
`remark` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_pk` (`id`) USING BTREE COMMENT '主键索引',
KEY `idx_msg_id_publickey_ver` (`msg_id`,`publickey_ver`) USING BTREE COMMENT '消息ID-公钥版本ID索引'
) ENGINE=InnoDB AUTO_INCREMENT=401 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='会话消息(加密)';
clear_text_message
: 保存会话内容的明文数据
CREATE TABLE `clear_text_message` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`msg_id` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '消息id,消息的唯一标识,企业可以使用此字段进行消息去重。',
`action` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '消息动作,目前有send(发送消息)/recall(撤回消息)/switch(切换企业日志)三种类型。',
`from_id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '消息发送方id。同一企业内容为userid,非相同企业为external_userid。消息如果是机器人发出,也为external_userid。',
`user_name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '发送方名称',
`to_list` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '消息接收方列表,可能是多个,同一个企业内容为userid,非相同企业为external_userid。',
`to_user_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '消息接收方(格式:工号-姓名)',
`room_id` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '群聊消息的群id。如果是单聊则为空。',
`room_name` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '群聊名称',
`msg_time` datetime DEFAULT NULL COMMENT '消息发送时间戳,utc时间,ms单位。',
`msg_type` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '消息类型',
`content`