企业微信会话存档功能

1.准备

1.从企业微信官方文档下载demo

官方文档地址
1.工具类Finance

package com.tencent.wework;

/**
 * @author byChen
 * @date 2022/1/26
 */
/* sdk返回数据
typedef struct Slice_t {
    char* buf;
    int len;
} Slice_t;

typedef struct MediaData {
    char* outindexbuf;
    int out_len;
    char* data;
    int data_len;
    int is_finish;
} MediaData_t;
*/

public class Finance {
   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);

   static {
//    System.loadLibrary("WeWorkFinanceSdk");
      /**
       * dll文件需要放在c:windows/system32文件夹下一份
       */
      String path = Finance.class.getResource("").getPath().replaceAll("%20", " ").replaceFirst("/", "").replace("/", "\\\\");
      System.load(path.concat("libcrypto-1_1-x64.dll"));
      System.load(path.concat("libcurl-x64.dll"));
      System.load(path.concat("libssl-1_1-x64.dll"));
      System.load(path.concat("WeWorkFinanceSdk.dll"));
   }
}


2.几个dll文件

①dll文件下载后放入电脑C盘C:\Windows\System32目录下一份
②项目中也同样需要放入一份

3.项目结构必须是这样的:
在这里插入图片描述

4.pom文件里面还要配置,配置打包时,将dll文件一块打包


   <!--指定打包文件-->
   <build>
   <resources>
      <resource>
         <directory>src/main/java</directory>
         <includes>
            <include>**/*.dll</include>
         </includes>
         <filtering>false</filtering>
      </resource>
   </resources>
   </build>

2.开始编码,提炼出工具类


/**
 * 企业微信会话存档工具类
 * @author byChen
 * @date 2022/1/26
 */
@Component
public class MsgFileUtils {
   /**
    * 企业微信里面的私钥内容
    */
   private static final String privKeyPEM = "-----BEGIN RSA PRIVATE KEY-----" +
         "你的私钥" +
         "-----END RSA PRIVATE KEY-----";

   /**
    * 根据资源名称,获取资源类型
    * 只处理图片、视频、语音、文件
    *
    * @param dto
    * @return
    */
   public static String getFileType(MsgConDto dto) {
      String fileType = "";

      JSONObject jsonObject = JSONObject.parseObject(dto.getStrData());
      String msgType = dto.getMsgType();
      switch (msgType) {
         case "image":
            fileType = ".jpg";
            break;
         case "voice":
            fileType = ".mp3";
            break;
         case "video":
            fileType = ".mp4";
            break;
         case "file":
            fileType = "." + jsonObject.getString("fileext");
            break;
         default:
            fileType = null;
            break;
      }
      return fileType;
   }


   /**
    * rsa私钥(企业微信的privKeyPEM)解密encrypt_random_key
    * (利用私钥解密公钥)
    *
    * @return
    */
   public static String decryptMsg(String encrypt_random_key) throws Exception {

      //64位解密后的字符串(需首先对每条消息的encrypt_random_key内容进行base64 decode,得到字符串str1.)
      byte[] decode = Base64.getDecoder().decode(encrypt_random_key);
      //获取私钥
      PrivateKey privateKey = RSAUtils.getPrivateKey(privKeyPEM);
      //RSA解密(使用RSA PKCS1算法对str1进行解密)
      Cipher cipher = Cipher.getInstance("RSA");
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      byte[] bytes = cipher.doFinal(decode);
      //(得到解密内容)
      String encrypt_key = new String(bytes);
      return encrypt_key;
   }

   /**
    * 拉取会话存档
    *
    * @param properties
    * @param seq
    * @throws Exception
    */
   public static List<MsgContent> PullChatStorage(QyWeChatConfigProperties properties, Integer seq) throws Exception {
      /**
       * 获取sdk对象,然后初始化函数
       * 使用sdk前需要初始化,初始化成功后的sdk可以一直使用
       * 如需并发调用sdk,建议每个线程持有一个sdk实例
       */
      long sdk = Finance.NewSdk();
      Finance.Init(sdk, properties.getCorpId(), properties.getSecret4());

      /**
       * 拉取会话内容前,参数准备
       */
      int limit = 100;//一次拉取多少条信息,最大1000
      String proxy = null;
      String passwd = null;
      int timeout = 2000;//超时时间
      /**
       * 每次使用GetChatData拉取存档前需要调用NewSlice获取一个slice用来接受产出值,
       * 在使用完slice中数据后,还需要调用FreeSlice释放。
       */
      long slice = Finance.NewSlice();
      int i = Finance.GetChatData(sdk, seq, limit, proxy, passwd, timeout, slice);
      //根据返回结果判断是不是成功
      if (i != 0) {
         System.out.println("getchatdata ret " + i);
         //释放
         Finance.FreeSlice(slice);
         System.out.println("拉取会话存档失败");
         return null;
      }

      /**
       * 将返回的slice对象转成JSON字符串
       * slice结构体:
       *  "errcode":0,
       *  "errmsg":"ok",
       *  "chatdata":[
       *  {"seq":196,
       *  "msgid":"CAQQ2fbb4QUY0On2rYSAgAMgip/yzgs=",
       *  "publickey_ver":3,
       *  "encrypt_random_key":"ftJ+uz3n/z1DsxlkwxNgE+mL38H42/KCvN8T60gbbtPD+Rta1hKTuQPzUzO6Hzne97MgKs7FfdDxDck/v8cDT6gUVjA2tZ/M7euSD0L66opJ/IUeBtpAtvgVSD5qhlaQjvfKJc/zPMGNK2xCLFYqwmQBZXbNT7uA69Fflm512nZKW/piK2RKdYJhRyvQnA1ISxK097sp9WlEgDg250fM5tgwMjujdzr7ehK6gtVBUFldNSJS7ndtIf6aSBfaLktZgwHZ57ONewWq8GJe7WwQf1hwcDbCh7YMG8nsweEwhDfUz+u8rz9an+0lgrYMZFRHnmzjgmLwrR7B/32Qxqd79A==",
       *  "encrypt_chat_msg":"898WSfGMnIeytTsea7Rc0WsOocs0bIAerF6de0v2cFwqo9uOxrW9wYe5rCjCHHH5bDrNvLxBE/xOoFfcwOTYX0HQxTJaH0ES9OHDZ61p8gcbfGdJKnq2UU4tAEgGb8H+Q9n8syRXIjaI3KuVCqGIi4QGHFmxWenPFfjF/vRuPd0EpzUNwmqfUxLBWLpGhv+dLnqiEOBW41Zdc0OO0St6E+JeIeHlRZAR+E13Isv9eS09xNbF0qQXWIyNUi+ucLr5VuZnPGXBrSfvwX8f0QebTwpy1tT2zvQiMM2MBugKH6NuMzzuvEsXeD+6+3VRqL"
       *  },
       *  {...},
       *  {...},
       *  ...拉取多条
       *  ]
       */
      String sliceStr = Finance.GetContentFromSlice(slice);
//    System.out.println("返回本次拉取消息的数据" + sliceStr);
      //转化成实体
      SliceDate sliceDate = JSONObject.parseObject(sliceStr, SliceDate.class);
      //释放
      Finance.FreeSlice(slice);
      /**
       * 遍历每一条未解密会话,进行解密、判断等操作
       */
      List<MsgContent> msgContentList=new ArrayList<>();
      for (ChatData chatData : sliceDate.getChatdata()) {
         String encrypt_random_key = chatData.getEncrypt_random_key();
         //此处需要先用rsa私钥解密encrypt_random_key后,作为encrypt_key参数传入sdk
         String encrypt_key = MsgFileUtils.decryptMsg(encrypt_random_key);
         long msg = Finance.NewSlice();
         //进行会话的解密
         Finance.DecryptData(sdk, encrypt_key, chatData.getEncrypt_chat_msg(), msg);
         //将解密的消息明文转化为字符串
         String s = Finance.GetContentFromSlice(msg);
//       System.out.println("解密后的消息明文为:" + s);
         //释放
         Finance.FreeSlice(msg);
         /**
          * 将消息明文,压制为实体
          */
         JSONObject content = JSONObject.parseObject(s);
         MsgContent msgContent = new MsgContent();
         if ("send".equals(content.get("action"))) {//发送消息
            //消息的seq值,标识消息的序号
            msgContent.setSeq(chatData.getSeq());
            //消息id
            msgContent.setMsgid(content.getString("msgid"));
            //消息动作
            msgContent.setMesAction(content.getString("action"));
            //存入发送人id(可用此id去表中获取到名字)
            msgContent.setMesFrom(content.getString("from"));
            //接收人id可以是列表,因此需要处理
            String tolist = content.getString("tolist");
            String replace = tolist.replace("[", "").replace("]", "").replace("\"", "");
            msgContent.setTolist(replace);
            msgContent.setRoomid(content.getString("roomid"));
            //存入创建时间
            String msgtime = content.getString("msgtime");
            Long time = Long.valueOf(msgtime);
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = new Date(time);
            String format = simpleDateFormat.format(date);
            msgContent.setMsgtime(format);
            //消息类型
            msgContent.setMsgtype(content.getString("msgtype"));
            switch (msgContent.getMsgtype()) {
               case "text":
                  //处理文本消息,对文本消息进行获取,其他类型消息直接返回全部字符串
                  String text = content.getString("text");
                  if (text != null) {
                     JSONObject jsonObject1 = JSONObject.parseObject(text);
                     String content1 = jsonObject1.getString("content");
                     if (content1 != null) {
                        //存入文本消息的文本内容
                        msgContent.setText(content1);
                     }
                  }
                  break;
               case "image":
                  msgContent.setStrData(content.getString("image"));
                  break;
               case "weapp":
                  msgContent.setStrData(content.getString("weapp"));
                  break;
               case "file":
                  msgContent.setStrData(content.getString("file"));
                  break;
               case "video":
                  msgContent.setStrData(content.getString("video"));
                  break;
               case "voice":
                  msgContent.setStrData(content.getString("voice"));
                  break;
               case "chatrecord":
                  msgContent.setText(content.getString("chatrecord"));
               case "link":
                  msgContent.setLink(content.getString("link"));
               default:
            }
            msgContentList.add(msgContent);
         }
      }
      //关闭SDK
      Finance.DestroySdk(sdk);
      System.out.println("----------------------scheduled tasks qywx data success-----------------------");
      return msgContentList;
   }
}

/**
 * 下载会话存档中的资源
 * @param sdkfileid
 * @param fileName
 * @param properties
 */
public static void downLodaFile(String sdkfileid,String fileName,QyWeChatConfigProperties properties){
   int ret = 0;
   long sdk = Finance.NewSdk();
   // 初始化
   Finance.Init(sdk, properties.getCorpId(), properties.getSecret4());
   String indexbuf = "";
   while (true) {
      long media_data = Finance.NewMediaData();
      ret = Finance.GetMediaData(sdk, indexbuf, sdkfileid, null, null, 3, media_data);
      if (ret != 0) {
         return;
      }
      System.out.printf("getmediadata outindex len:%d, data_len:%d, is_finis:%d\n",
            Finance.GetIndexLen(media_data), Finance.GetDataLen(media_data),
            Finance.IsMediaDataFinish(media_data));
      try {
         FileOutputStream outputStream = new FileOutputStream(new File("H:\\scrm-chat-file\\" + fileName), true);
         outputStream.write(Finance.GetData(media_data));
         outputStream.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
      if (Finance.IsMediaDataFinish(media_data) == 1) {
         Finance.FreeMediaData(media_data);
         break;
      } else {
         indexbuf = Finance.GetOutIndexBuf(media_data);
         Finance.FreeMediaData(media_data);
      }
   }
   Finance.DestroySdk(sdk);
}

直接复制,替换必要参数就行

3.调用工具类

1.调用拉取存档

/**
 * 拉取会话记录
 *    从上次拉取最新开始算起,每次访问拉取100条
 * @return
 * @throws WxErrorException
 * @throws IOException
 */
@RequestMapping(value = "/uploadImg", method = RequestMethod.GET)
public R uploadImg(Page<MsgContent> page) throws Exception {
   //获取本次拉取开始的位置
   int seq = msgContentService.getLastSeq() == null ? 0 : msgContentService.getLastSeq();// 从第几条开始拉取
   //工具类进行拉取
   List<MsgContent> msgContents = MsgFileUtils.PullChatStorage(properties, seq);
   if (msgContents==null){
      return R.ok("拉取存档失败");
   }
   //拉取的存入数据库
   for (MsgContent msgContent : msgContents) {
      msgContentService.saveCheckIfHave(msgContent);
   }
   return R.ok("拉取完成");
}

getLastSeq:获取应该从那一条开始拉取

  @Select("SELECT seq FROM msg_content ORDER BY id DESC limit 1")
   Integer getLastSeq();

saveCheckIfHave:判断是否存在,插入

/**
 * 判断本次拉取的数据是否以及存在
 *        不存在才插入
 * @param msgContent
 */
@Override
public void saveCheckIfHave(MsgContent msgContent) {
   MsgContent msgContent1 = baseMapper.selectOne(Wrappers.<MsgContent>lambdaQuery()
         .eq(MsgContent::getMsgid, msgContent.getMsgid()));
   if (msgContent1==null){
      baseMapper.insert(msgContent);
   }
}

properties:存在nacos上的企业微信参数映射类


/**
 * 企业微信参数,对应nacos配置映射
 * @author byChen
 * @date 2022/1/18
 */
@Data
@RefreshScope
@Configuration
@ConfigurationProperties(prefix = "qychat")
public class QyWeChatConfigProperties {
   /**
    * 企业id
    */
   private String corpId;
   /**
    * 通讯录应用id
    */
   private Integer agentId1;
   /**
    * 外部联系人应用id
    */
   private Integer agentId2;
   /**
    * 消息推送应用id
    */
   private Integer agentId3;
   /**
    * 通讯录应用密钥
    */
   private String secret1;
   /**
    * 外部联系人应用密钥
    */
   private String secret2;
   /**
    * 消息推送应用密钥
    */
   private String secret3;
   /**
    * 会话存档密钥
    */
   private String secret4;
   /**
    * 通讯录回调应用密钥
    */
   private String EncodingAESKey1;
   /**
    * 外部联系人回调应用密钥
    */
   private String EncodingAESKey2;
   /**
    * 通讯录回调应用密钥
    */
   private String token1;
   /**
    * 外部联系人回调应用密钥
    */
   private String token2;



}


2.调用下载存档文件资源

/**
 * 下载会话中存在的文件
 * fileName  会话返回的媒体图片资源的md5值+格式
 * sdkfileid  会话返回的媒体资源的id信息
 *
 * @param
 * @param
 */
@GetMapping("/downFile")
public void downLodaFile(@RequestBody MsgConDto dto) {
   //获取必要参数
   JSONObject content = JSONObject.parseObject(dto.getStrData());
   //会话返回的媒体资源的id信息
   String sdkfileid = content.getString("sdkfileid");
   //拼接成的文件名
   String fileName = content.getString("md5sum") + MsgFileUtils.getFileType(dto);
   //进行下载 下载目录为  H:\scrm-chat-file\
   MsgFileUtils.downLodaFile(sdkfileid, fileName, properties);
}

MsgConDto

/**
 * @author byChen
 * @date 2022/1/26
 */
@Data
public class MsgConDto {
   /**
    * 资源数据
    */
   private String strData;
   /**
    * 消息类型
    */
   private String msgType;
}

完成

Spring Boot是一种用于开发Java应用程序的开源框架,它简化了基于Spring框架的应用程序的配置和部署。企业微信是一款为企业提供即时通讯和协同办公的应用,它能轻松满足企业的沟通和协作需求。 为了在Spring Boot应用程序中引入企业微信会话存档SDK,需要执行以下步骤: 1. 下载企业微信会话存档SDK:你可以从企业微信官方网站或其他可信来源下载适用于Java企业微信会话存档SDK。 2. 引入SDK依赖:将下载的SDK文件导入Spring Boot应用程序的依赖管理系统中。可以使用Maven或Gradle等构建工具来管理依赖,并在配置文件中指定SDK的版本号。 3. 创建企业微信会话存档SDK的配置文件:在Spring Boot的配置文件中,添加企业微信会话存档SDK所需的配置参数,如企业微信的Corpid、Secret和Agentid等。 4. 创建企业微信会话存档SDK的服务类:在Spring Boot应用程序中创建一个服务类,用于调用企业微信会话存档SDK的接口。根据业务需求,可以根据文档提供的接口进行数据的存储、获取等操作。 5. 在应用程序中调用企业微信会话存档服务:在需要使用企业微信会话存档功能的地方,通过注入企业微信会话存档服务的实例,调用相应的方法来实现会话存档功能。 需要注意的是,引入企业微信会话存档SDK前,要确保企业微信账号已通过认证并开通了相应的权限。 以上是使用Spring Boot引入企业微信会话存档SDK的基本步骤,根据具体的需求,可能还需要对业务逻辑进行进一步的处理和优化。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值