commit fa262e97f22f2de95d89fdd9849ce8ecd0d0a5d5
Author: 杨建炊 <924182103@qq.com>
Date: Wed Sep 24 17:16:04 2025 +0800
feat: initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..480bdf5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+.kotlin
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..bf2de99
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..4140949
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..0eefe32
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..9e1b5e9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..9661ac7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..bd6121b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,62 @@
+
+
+ 4.0.0
+
+ org.shop
+ crop
+ 1.0-SNAPSHOT
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.3.RELEASE
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+ com.alibaba
+ fastjson
+ 1.2.83
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.77
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.5.6
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/tencent/wework/Finance.java b/src/main/java/com/tencent/wework/Finance.java
new file mode 100644
index 0000000..e72e221
--- /dev/null
+++ b/src/main/java/com/tencent/wework/Finance.java
@@ -0,0 +1,128 @@
+package com.tencent.wework;
+
+/* 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");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/Application.java b/src/main/java/org/shop/crop/Application.java
new file mode 100644
index 0000000..e62c2dc
--- /dev/null
+++ b/src/main/java/org/shop/crop/Application.java
@@ -0,0 +1,15 @@
+package org.shop.crop;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * @author god23bin
+ */
+@SpringBootApplication
+public class Application {
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/CropController.java b/src/main/java/org/shop/crop/CropController.java
new file mode 100644
index 0000000..d05dc14
--- /dev/null
+++ b/src/main/java/org/shop/crop/CropController.java
@@ -0,0 +1,114 @@
+package org.shop.crop;
+
+import com.alibaba.fastjson.JSON;
+import com.tencent.wework.Finance;
+import lombok.extern.slf4j.Slf4j;
+import org.shop.crop.dto.BaseMessageDto;
+import org.shop.crop.dto.ChatData;
+import org.shop.crop.dto.Message;
+import org.shop.crop.dto.MessagePullResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.StreamUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@Slf4j
+@RestController
+public class CropController {
+
+ @Autowired
+ Long sdk;
+
+ @Autowired
+ private Environment environment; // nul
+
+ @GetMapping("/")
+ public String index() {
+ return "Hello, World!";
+ }
+
+ @GetMapping("/crop/chatData")
+ private List chatData(Long startSeq) throws IOException {
+ long limit = 1000;
+ long slice = Finance.NewSlice();
+ int ret = Finance.GetChatData(sdk, startSeq, limit, "", "", 1000, slice);
+ if (ret != 0) {
+ System.out.println("调用sdk拉取消息接口失败,失败消息为 ret = " + ret);
+ Finance.FreeSlice(slice);
+ return null;
+ }
+ String contentResult = Finance.GetContentFromSlice(slice);
+ log.info("内容字符串" + contentResult);
+ return decodeChatData(contentResult);
+ }
+
+ /**
+ * 解密chatData数据
+ *
+ * @param contentResult 拉取到的JSON原文
+ */
+ public List decodeChatData(String contentResult) throws IOException {
+ // 1.基础信息解析
+ List messageList = new ArrayList<>();
+ MessagePullResponse messagePullResponse = JSON.parseObject(contentResult, MessagePullResponse.class);
+ if (messagePullResponse == null || messagePullResponse.getChatdata() == null) {
+ return Collections.emptyList();
+ }
+ List chatdataList = messagePullResponse.getChatdata();
+ // 2.解密数据
+ ClassPathResource resource = new ClassPathResource("private_key.pem");
+ if (!resource.exists()) {
+ return Collections.emptyList();
+ }
+ String privateKey = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
+ for (ChatData chatData : chatdataList) {
+ try {
+ // 解密ChatData数据
+ String plainTextJson = decrypt(chatData, privateKey);
+ BaseMessageDto messageDto = JSON.parseObject(plainTextJson, BaseMessageDto.class);
+ // 转换dto数据到entity
+ Message message = messageDto.convertToMessage();
+ message.setSeq(chatData.getSeq());
+ message.setOriginContent(plainTextJson);
+ messageList.add(message);
+ } catch (Exception e) {
+ log.error("解密失败{}", e.getMessage());
+ // 收集失败情况数据
+ e.printStackTrace();
+
+ }
+ }
+
+ // 4.保存消息和失败任务
+ return messageList;
+ }
+
+ /**
+ * 解密数据
+ *
+ * @param chatData 待解密数据
+ * @param privateKeyStr 私钥字符串
+ * @return 解密后的原文
+ */
+ private String decrypt(ChatData chatData, String privateKeyStr) throws Exception {
+ // 1.解密EncryptRandomKey
+ String randomKey = EncodeUtils.decryptRandomKey(chatData.getEncryptRandomKey(), privateKeyStr);
+ // 2.调用SDK方法解密密文数据
+ long msg = Finance.NewSlice();
+ Finance.DecryptData(sdk, randomKey, chatData.getEncryptChatMsg(), msg);
+ String plaintext = Finance.GetContentFromSlice(msg);
+ Finance.FreeSlice(msg);
+ return plaintext;
+ }
+
+
+}
diff --git a/src/main/java/org/shop/crop/EncodeUtils.java b/src/main/java/org/shop/crop/EncodeUtils.java
new file mode 100644
index 0000000..ca576a5
--- /dev/null
+++ b/src/main/java/org/shop/crop/EncodeUtils.java
@@ -0,0 +1,55 @@
+package org.shop.crop;
+
+import javax.crypto.Cipher;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.util.Base64;
+
+/**
+ * @description 加解密工具
+ * @date 2024/11/21 9:22
+ */
+public class EncodeUtils {
+
+ public static PrivateKey getPrivateKey(String privKeyPEM) throws Exception {
+ String pem = privKeyPEM.trim();
+ // 清理头尾和空白
+ if (pem.contains("BEGIN PRIVATE KEY")) {
+ // PKCS#8 格式(使用 URL-safe Base64)
+ pem = pem
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replaceAll("\\s+", "");
+ } else {
+ throw new IllegalArgumentException("Invalid private key format");
+ }
+ System.out.println("Raw private key: " + pem);
+
+ // 关键:使用 url decoder!
+ byte[] derBytes = Base64.getDecoder().decode(pem);
+
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+
+ // PKCS#8
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(derBytes);
+ return keyFactory.generatePrivate(spec);
+ }
+
+ // ✅ 修复命名和字符集
+ public static String decryptRandomKey(String encryptedRandomKeyBase64, String privateKeyPem) throws Exception {
+ PrivateKey privateKey = getPrivateKey(privateKeyPem);
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+
+ byte[] encryptedBytes = Base64.getDecoder().decode(encryptedRandomKeyBase64);;
+ byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
+
+ return new String(decryptedBytes, StandardCharsets.UTF_8); // 显式指定 UTF-8
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/Main.java b/src/main/java/org/shop/crop/Main.java
new file mode 100644
index 0000000..1d7701d
--- /dev/null
+++ b/src/main/java/org/shop/crop/Main.java
@@ -0,0 +1,17 @@
+package org.shop.crop;
+
+//TIP 要运行代码,请按 或
+// 点击装订区域中的 图标。
+public class Main {
+ public static void main(String[] args) {
+ //TIP 当文本光标位于高亮显示的文本处时按
+ // 查看 IntelliJ IDEA 建议如何修正。
+ System.out.printf("Hello and welcome!");
+
+ for (int i = 1; i <= 5; i++) {
+ //TIP 按 开始调试代码。我们已经设置了一个 断点
+ // 但您始终可以通过按 添加更多断点。
+ System.out.println("i = " + i);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/MyConfig.java b/src/main/java/org/shop/crop/MyConfig.java
new file mode 100644
index 0000000..9f32ea5
--- /dev/null
+++ b/src/main/java/org/shop/crop/MyConfig.java
@@ -0,0 +1,26 @@
+package org.shop.crop;
+
+import com.tencent.wework.Finance;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class MyConfig {
+
+ @Bean
+ public long sdk() {
+ System.out.println("Initializing SDK...");
+ long sdk = Finance.NewSdk();
+ int init = Finance.Init(
+ sdk,
+ "ww64f47cd0afc456a5",
+ "mEizahrSF6axdfWtSK_f73a3j6-sV02hhyGG7ogmTpM"
+ );
+
+ if (init != 0) {
+ Finance.DestroySdk(sdk);
+ throw new RuntimeException("初始化sdk失败,失败消息为:ret = " + init);
+ }
+ return sdk;
+ }
+}
diff --git a/src/main/java/org/shop/crop/dao/CropMessage.java b/src/main/java/org/shop/crop/dao/CropMessage.java
new file mode 100644
index 0000000..1f3a955
--- /dev/null
+++ b/src/main/java/org/shop/crop/dao/CropMessage.java
@@ -0,0 +1,112 @@
+package org.shop.crop.dao;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import org.apache.ibatis.annotations.Mapper;
+import org.shop.crop.dto.Message;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+@Data
+@TableName("crop_message")
+public class CropMessage {
+
+
+ /**
+ * 主键 ID(自增)
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 消息唯一标识(全局 ID)
+ */
+ @TableField("msg_id")
+ private String msgId;
+
+ /**
+ * 消息动作(如 send, reply, delete)
+ */
+ @TableField("action")
+ private String action;
+
+ /**
+ * 发送方 ID
+ */
+ @TableField("from")
+ private String from; // 注意:'from' 是关键字,不能直接写,所以用注解指定
+
+ /**
+ * 接收方列表(JSON 或逗号分隔)
+ */
+ @TableField("tolist")
+ private String tolist;
+
+ /**
+ * 群聊消息的房间 ID
+ */
+ @TableField("roomid")
+ private String roomid;
+
+ /**
+ * 消息序号(用于排序)
+ */
+ @TableField("seq")
+ private Long seq;
+
+ /**
+ * 消息类型(如 text, image, voice)
+ */
+ @TableField("msgtype")
+ private String msgtype;
+
+ /**
+ * 解密后的明文内容(TEXT 类型)
+ */
+ @TableField("content")
+ private String content;
+
+ /**
+ * 消息发送时间
+ */
+ @TableField("msgtime")
+ private LocalDateTime msgtime;
+
+ /**
+ * 消息状态(是否已处理)
+ */
+ @TableField("status")
+ private Integer status;
+
+ /**
+ * 创建时间(自动填充)
+ */
+ @TableField(fill = FieldFill.INSERT)
+ private LocalDateTime createdTime;
+
+ /**
+ * 逻辑删除标志(0:正常,1:已删除)
+ */
+ @TableLogic
+ @TableField("is_deleted")
+ private Integer isDeleted;
+
+ /**
+ * 创建时间(备用字段,可选)
+ */
+ @TableField("gmt_created")
+ private LocalDateTime gmtCreated;
+
+ /**
+ * 修改时间(自动填充)
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime gmtModified;
+
+
+
+}
diff --git a/src/main/java/org/shop/crop/dto/BaseMessageDto.java b/src/main/java/org/shop/crop/dto/BaseMessageDto.java
new file mode 100644
index 0000000..292922d
--- /dev/null
+++ b/src/main/java/org/shop/crop/dto/BaseMessageDto.java
@@ -0,0 +1,90 @@
+package org.shop.crop.dto;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+/**
+ * @description 拉取基础信息dto
+ * @date 2024/11/21 17:50
+ */
+@Data
+public class BaseMessageDto {
+ /**
+ * 消息id,消息的唯一标识
+ */
+ @JSONField(name = "msgid")
+ private String msgId;
+
+ /**
+ * 消息动作
+ */
+ @JSONField(name = "action")
+ private String action;
+
+ /**
+ * 消息发送方id
+ */
+ @JSONField(name = "from")
+ private String fromId;
+
+ /**
+ * 消息接收方列表
+ */
+ @JSONField(name = "tolist")
+ private List toList;
+
+ /**
+ * 群聊消息的id
+ */
+ @JSONField(name = "roomid")
+ private String roomId;
+
+
+ /**
+ * 消息类型
+ */
+ @JSONField(name = "msgtype")
+ private String msgType;
+
+ /**
+ * 消息发送的时间
+ */
+ @JSONField(name = "msgtime")
+ private Long msgTime;
+
+ // getter setter
+
+ public Message convertToMessage(){
+ Message message = new Message();
+ message.setMsgId(this.msgId);
+ message.setAction(this.action);
+ message.setFromId(this.fromId);
+ message.setMsgTime(this.msgTime);
+ message.setMsgType(this.msgType);
+ message.setRoomId(this.roomId);
+ // id数组转为字符串
+ message.setToList(JSON.toJSONString(this.toList));
+ // 格式化时间
+ if(this.getMsgTime() != null){
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");;
+ LocalDateTime dateTime = LocalDateTime.ofInstant(
+ Instant.ofEpochSecond(this.getMsgTime()),
+ ZoneId.systemDefault()
+ );
+ String timeStr=dateTime.format(formatter);
+ message.setMsgTimeStr(timeStr);
+ }
+
+
+ return message;
+ }
+
+}
diff --git a/src/main/java/org/shop/crop/dto/ChatData.java b/src/main/java/org/shop/crop/dto/ChatData.java
new file mode 100644
index 0000000..6bd201b
--- /dev/null
+++ b/src/main/java/org/shop/crop/dto/ChatData.java
@@ -0,0 +1,18 @@
+package org.shop.crop.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+@Data
+public class ChatData {
+ private Long seq;
+ private String msgid;
+ @JsonProperty("publickey_ver")
+ private int publickeyVer;
+ @JsonProperty("encrypt_random_key")
+ private String encryptRandomKey;
+ @JsonProperty("encrypt_chat_msg")
+ private String encryptChatMsg;
+
+ // Getters and Setters
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/dto/Message.java b/src/main/java/org/shop/crop/dto/Message.java
new file mode 100644
index 0000000..103a378
--- /dev/null
+++ b/src/main/java/org/shop/crop/dto/Message.java
@@ -0,0 +1,81 @@
+package org.shop.crop.dto;
+
+import lombok.Data;
+
+/**
+ * @description 消息基础信息
+ * @date 2024/11/21 10:46
+ */
+@Data
+public class Message {
+
+ /**
+ * 自增主键
+ */
+ private Long id;
+
+ /**
+ * 消息id,消息的唯一标识
+ */
+ private String msgId;
+
+ /**
+ * 消息动作
+ */
+ private String action;
+
+ /**
+ * 消息发送方id
+ */
+ private String fromId;
+ /**
+ * 消息发送方群名称
+ */
+ private String fromNickName;
+ /**
+ * 消息发送方账户名称
+ */
+ private String fromName;
+
+ /**
+ * 消息接收方列表
+ */
+ private String toList;
+
+ /**
+ * 群聊消息的id
+ */
+ private String roomId;
+
+ /**
+ * 消息序号
+ */
+ private Long seq;
+
+ /**
+ * 消息类型
+ */
+ private String msgType;
+
+ /**
+ * 解密后的明文消息内容
+ */
+ private String originContent;
+
+ /**
+ * 消息发送的时间
+ */
+ private Long msgTime;
+
+ /**
+ * 消息发送的时间(格式化)
+ */
+ private String msgTimeStr;
+
+ /**
+ * 消息状态(是否被处理)
+ */
+ private Integer status;
+
+ // Getters and Setters
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/dto/MessagePullResponse.java b/src/main/java/org/shop/crop/dto/MessagePullResponse.java
new file mode 100644
index 0000000..24bd74d
--- /dev/null
+++ b/src/main/java/org/shop/crop/dto/MessagePullResponse.java
@@ -0,0 +1,15 @@
+package org.shop.crop.dto;
+
+
+import lombok.Data;
+
+import javax.annotation.Resource;
+import java.util.List;
+@Data
+public class MessagePullResponse {
+ private int errcode;
+ private String errmsg;
+ private List chatdata;
+
+ // Getters and Setters
+}
\ No newline at end of file
diff --git a/src/main/java/org/shop/crop/mapper/CropMessageMapper.java b/src/main/java/org/shop/crop/mapper/CropMessageMapper.java
new file mode 100644
index 0000000..52c9c4e
--- /dev/null
+++ b/src/main/java/org/shop/crop/mapper/CropMessageMapper.java
@@ -0,0 +1,9 @@
+package org.shop.crop.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.shop.crop.dao.CropMessage;
+
+@Mapper
+public interface CropMessageMapper extends BaseMapper {
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..a30a91f
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1 @@
+server.port=8085
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..0eb3f73
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,17 @@
+spring:
+ datasource:
+ url: jdbc:mysql://121.199.65.65:3306/shop?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
+ username: sktod_test
+ password: JlZOLKtvma8Z19XF
+ driver-class-name: com.mysql.cj.jdbc.Driver
+
+mybatis-plus:
+ mapper-locations: classpath:mapper/*.xml
+ type-aliases-package: com.example.demo.entity
+ configuration:
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL
+ global-config:
+ db-config:
+ id-type: auto
+ logic-delete-value: 1
+ logic-not-delete-value: 0
\ No newline at end of file
diff --git a/src/main/resources/private_key.pem b/src/main/resources/private_key.pem
new file mode 100644
index 0000000..191a8ee
--- /dev/null
+++ b/src/main/resources/private_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbQp/SsUQgkhbX
+tzdl/IJVkX9eYD4Qusd8nBhCRYkN6VfQpONX1r9qpvQOkXy0D0FDMpJnRnvDZLQ3
+INduo8caj/101DxK6NHc5mYIJ9U2G561JyvDtkiS68qL1bepCK729lZSnvZBs6jr
+A4ARN0DTtiFsjqK26oZXP4NqnfI8lYyMjaEvXHpQfA7+MI6LAyQAvC0KtM1681aM
++DAlsma88FRaPSEKvfTkWKvKgZUrfCUuDZscvczROlgNpEYHL4tNVPhQxaLOEB9m
+iTfsAIVLdadJBVbhUq3qsTGBKa+Bo3XKMgkHVQl9KJaPv3r4SJ8cyv8mWCp+8VQ0
+C2Mmw9rLAgMBAAECggEAODiWq5s5mVJsWgbIRTXgo5lJvATYlUiXYzvROpkC4hbN
+phnJk5nkfi/uD5g9jhwFQ3pBsQqRLJJzZVld9Cg5ovV964mKJjkZqtdHHVs9YADo
+CpJPNgFVDZs5Ve7Ih6bK2ldi3g+VnFY5f31nxsJQtJiUbHYMgfOT2+ZUx69VUDQd
+AAB1D/V0SqbIBO9+MTInkfUeN91p+HPWNyLssMDOn9LWmD6N2irzpS6Uo47vpGT2
+lyFUcTZ8iNebebLdRPTmv1wx7mziPc0CVjsKhAwSDwZJHdlUOiWEipDnJ2Kggy6i
+T9tl+XpfDU3ymel7mg6MjTHErSA42K7IM2cYHYx4JQKBgQD1qgJndwxtF6NOMW6+
+zDYQ454F3iuc3U6N7jNx5T4MxkR5TfIlwQypYEpxMzYI+mJUmrBebTM+eoC7i1QG
+oOA+r/5DBsK8MTK/W9uyAyMlH+dEmvpkxWIKxS80U63m9ZuqAoXmSnWTcERD2nTg
+eKnbPHJ23cZM+eSM1FzZKR6jzQKBgQDkfDmpXR0wZ0VwBpm2sKqO1cgZ3BVC9adN
+ZngWl1OVvWZMY0jTEwVKYvDmBGpLX6eFk8D+8v+gKkkG87m8b4A6UnrJFXjXDGOf
+cSitwLq7Xfuv97M2Pl9sfkMRs3w6CxZn0IM2SuMAqXeTlZiEhdk9P+lbQs4lkBXC
+0ta9xtMQ9wKBgAFhIwJRuNAkAda+kFCh9OvDW2/M/5Gx7G7/l2pcTyzjMB8+QM3V
+d6y7za81Cmiif0raU4M0OcVwCqkC70XaQWgYtOTLdOx5mj9Y1LL7MYdT1oicFiva
+r+I/mTRCBAf3+RfLS9UvNdnOF5QJ9f4Mr2d7v2jLdgGYpZ/k0WCmynVBAoGBAL9i
+Kq5pl1yG9EvMy46SVpsECPNYPNr0h2m2zhomMVY46mpLs6FRylmvnJce1aZC/kBO
+LheflW0kXj0bg9WKE/9GPSEYAjsnXn8cOvWnfQIazApuUce64RUwgZ9fF0xLIz5c
+Erw0aAaS7N1LLhC7SOccFKxjvecK1J93GXanBv6rAoGBAMtwLcBLHqbzefawkkow
+UoFdz64nM5X47mFVyeVeIW85rUV6lLS2m1WtLYA12qRZ3nx4ls268P9CUYAH7Vh2
+m/NWH3UJvNbYR1Z3SPs2qUfTVQ4cJC23bp0ij4J2GMBNGA8m+RTrQK7gTSofvG3d
+LzNelpso5YqSOUw3nfTiNLBn
+-----END PRIVATE KEY-----
\ No newline at end of file
diff --git a/src/main/resources/wechat.sdk/WeWorkFinanceSdk.dll b/src/main/resources/wechat.sdk/WeWorkFinanceSdk.dll
new file mode 100644
index 0000000..4ce7286
Binary files /dev/null and b/src/main/resources/wechat.sdk/WeWorkFinanceSdk.dll differ
diff --git a/src/main/resources/wechat.sdk/libWeWorkFinanceSdk_Java.so b/src/main/resources/wechat.sdk/libWeWorkFinanceSdk_Java.so
new file mode 100644
index 0000000..d506486
Binary files /dev/null and b/src/main/resources/wechat.sdk/libWeWorkFinanceSdk_Java.so differ
diff --git a/src/main/resources/wechat.sdk/libcrypto-3-x64.dll b/src/main/resources/wechat.sdk/libcrypto-3-x64.dll
new file mode 100644
index 0000000..b1a6dbe
Binary files /dev/null and b/src/main/resources/wechat.sdk/libcrypto-3-x64.dll differ
diff --git a/src/main/resources/wechat.sdk/libcurl-x64.dll b/src/main/resources/wechat.sdk/libcurl-x64.dll
new file mode 100644
index 0000000..8b79e41
Binary files /dev/null and b/src/main/resources/wechat.sdk/libcurl-x64.dll differ