Compare commits
2 Commits
cca6901cc6
...
acb8a77c53
| Author | SHA1 | Date | |
|---|---|---|---|
| acb8a77c53 | |||
| fa262e97f2 |
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -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
|
||||
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
5
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
5
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="PROJECT_PROFILE" />
|
||||
</settings>
|
||||
</component>
|
||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="15" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
62
pom.xml
Normal file
62
pom.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.shop</groupId>
|
||||
<artifactId>crop</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.1.3.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Web 依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.83</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.77</version>
|
||||
</dependency>
|
||||
<!-- MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis-Plus Starter -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.6</version> <!-- 推荐使用最新稳定版 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
128
src/main/java/com/tencent/wework/Finance.java
Normal file
128
src/main/java/com/tencent/wework/Finance.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
15
src/main/java/org/shop/crop/Application.java
Normal file
15
src/main/java/org/shop/crop/Application.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
114
src/main/java/org/shop/crop/CropController.java
Normal file
114
src/main/java/org/shop/crop/CropController.java
Normal file
@ -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<Message> 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<Message> decodeChatData(String contentResult) throws IOException {
|
||||
// 1.基础信息解析
|
||||
List<Message> messageList = new ArrayList<>();
|
||||
MessagePullResponse messagePullResponse = JSON.parseObject(contentResult, MessagePullResponse.class);
|
||||
if (messagePullResponse == null || messagePullResponse.getChatdata() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ChatData> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
55
src/main/java/org/shop/crop/EncodeUtils.java
Normal file
55
src/main/java/org/shop/crop/EncodeUtils.java
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
17
src/main/java/org/shop/crop/Main.java
Normal file
17
src/main/java/org/shop/crop/Main.java
Normal file
@ -0,0 +1,17 @@
|
||||
package org.shop.crop;
|
||||
|
||||
//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
|
||||
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
//TIP 当文本光标位于高亮显示的文本处时按 <shortcut actionId="ShowIntentionActions"/>
|
||||
// 查看 IntelliJ IDEA 建议如何修正。
|
||||
System.out.printf("Hello and welcome!");
|
||||
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
//TIP 按 <shortcut actionId="Debug"/> 开始调试代码。我们已经设置了一个 <icon src="AllIcons.Debugger.Db_set_breakpoint"/> 断点
|
||||
// 但您始终可以通过按 <shortcut actionId="ToggleLineBreakpoint"/> 添加更多断点。
|
||||
System.out.println("i = " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/main/java/org/shop/crop/MyConfig.java
Normal file
26
src/main/java/org/shop/crop/MyConfig.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
112
src/main/java/org/shop/crop/dao/CropMessage.java
Normal file
112
src/main/java/org/shop/crop/dao/CropMessage.java
Normal file
@ -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;
|
||||
|
||||
|
||||
|
||||
}
|
||||
90
src/main/java/org/shop/crop/dto/BaseMessageDto.java
Normal file
90
src/main/java/org/shop/crop/dto/BaseMessageDto.java
Normal file
@ -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<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/org/shop/crop/dto/ChatData.java
Normal file
18
src/main/java/org/shop/crop/dto/ChatData.java
Normal file
@ -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
|
||||
}
|
||||
81
src/main/java/org/shop/crop/dto/Message.java
Normal file
81
src/main/java/org/shop/crop/dto/Message.java
Normal file
@ -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
|
||||
}
|
||||
15
src/main/java/org/shop/crop/dto/MessagePullResponse.java
Normal file
15
src/main/java/org/shop/crop/dto/MessagePullResponse.java
Normal file
@ -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> chatdata;
|
||||
|
||||
// Getters and Setters
|
||||
}
|
||||
@ -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<CropMessage> {
|
||||
}
|
||||
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
||||
server.port=8085
|
||||
17
src/main/resources/application.yml
Normal file
17
src/main/resources/application.yml
Normal file
@ -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
|
||||
28
src/main/resources/private_key.pem
Normal file
28
src/main/resources/private_key.pem
Normal file
@ -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-----
|
||||
BIN
src/main/resources/wechat.sdk/WeWorkFinanceSdk.dll
Normal file
BIN
src/main/resources/wechat.sdk/WeWorkFinanceSdk.dll
Normal file
Binary file not shown.
BIN
src/main/resources/wechat.sdk/libWeWorkFinanceSdk_Java.so
Normal file
BIN
src/main/resources/wechat.sdk/libWeWorkFinanceSdk_Java.so
Normal file
Binary file not shown.
BIN
src/main/resources/wechat.sdk/libcrypto-3-x64.dll
Normal file
BIN
src/main/resources/wechat.sdk/libcrypto-3-x64.dll
Normal file
Binary file not shown.
BIN
src/main/resources/wechat.sdk/libcurl-x64.dll
Normal file
BIN
src/main/resources/wechat.sdk/libcurl-x64.dll
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user