feat: initial commit
This commit is contained in:
commit
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