Merge pull request #246 from PathfinderAx/master

feat(0): [java]-[wechat-ferry-mvn]-基础功能完善
This commit is contained in:
Changhua 2024-10-04 21:05:01 +08:00 committed by GitHub
commit b97d899f02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 5876 additions and 6534 deletions

View File

@ -2,6 +2,9 @@
⚠️ **只支持 Windows** ⚠️ ⚠️ **只支持 Windows** ⚠️
`声明:` 本项目是基于 clients/java/wcferry 项目改造,随着时间推进,项目结构和代码规范逐渐产生分离,使用此项目的人员可参考之前的项目
我们在开发时请尽量保持注释的完整性,便于阅读维护
## 快速使用 ## 快速使用
### 环境准备 ### 环境准备
@ -20,11 +23,11 @@
可以直接以WeChatFerry为根目录打开 可以直接以WeChatFerry为根目录打开
或者以WeChatFerry/clients/java/wcferry-mvn为根目录打开 或者以WeChatFerry/clients/java/wechat-ferry-mvn为根目录打开
### 添加Maven ### 添加Maven
找到 WeChatFerry/clients/java/wcferry-mvn/pom.xml 文件右键添加到Maven中会自动下载依赖 找到 WeChatFerry/clients/java/wechat-ferry-mvn/pom.xml 文件右键添加到Maven中会自动下载依赖
### 替换对应版本的dll ### 替换对应版本的dll
@ -38,16 +41,17 @@
```yaml ```yaml
# 本服务参数 # 本服务参数
wcferry: wechat:
ferry:
# DLL文件位置 # DLL文件位置
dll-path: E:\WeChatFerry\clients\java\wcferry-mvn\dll\sdk.dll dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll
# socket端口 # socket端口
socket-port: 10086 socket-port: 10086
``` ```
### 编译运行 ### 编译运行
找到 src/main/java/com/iamteer/WcferryApplication.java 类 找到 src/main/java/com/wechat/ferry/WeChatFerryApplication.java 类
直接启动即可 直接启动即可
@ -71,24 +75,29 @@ swagger地址http://localhost:9201/swagger-ui/index.html
### 模块结构 ### 模块结构
``` ```
wcferry-mvn wechat-ferry-mvn
├─dll 核心dll ├─dll 核心dll
│ ├─sdk.dll sdk文件 │ ├─sdk.dll sdk文件
│ └─readme.txt 本目录说明文件 │ └─readme.txt 本目录说明文件
├─src 源 ├─src 源
│ ├─main 重启命令 │ ├─main 重启命令
│ │ ├─java(com.iamteer) java代码目录 │ │ ├─java(com.wechat.ferry) java代码目录
│ │ │ ├─config 配置 │ │ │ ├─config 配置
│ │ │ ├─entity 实体 │ │ │ ├─entity 聚合模型
│ │ │ ├─runner 运行(程序启动中与启动后的自动化任务都放置于此) │ │ │ │ ├─dto DTO模型
│ │ │ │ ├─po 实体模型
│ │ │ │ └─vo 视图层返回体目录
│ │ │ ├─enums 枚举
│ │ │ ├─handle 处理层
│ │ │ ├─service 业务接口 │ │ │ ├─service 业务接口
│ │ │ │ └─impl 业务实现类 │ │ │ │ └─impl 业务实现类
│ │ │ ├─Client.java socket客户端 │ │ │ ├─utils 工具类
│ │ │ └─WcferryApplication.java 启动类 │ │ │ └─WcferryApplication.java 启动类
│ │ │
│ │ │resources 资源目录 │ │ │resources 资源目录
│ │ │ ├─libs 本程序内置依赖包 │ │ │ ├─libs 本程序内置依赖包
│ │ │ ├─proto proto文件 │ │ │ ├─proto proto文件(此目录打包将被排除)
│ │ │ ├─win32-x86-64 依赖程序 │ │ │ ├─win32-x86-64 依赖程序
│ │ │ ├─application.yml 本程序主配置文件 │ │ │ ├─application.yml 本程序主配置文件
│ │ │ └─logback-spring.xml 日志配置文件 │ │ │ └─logback-spring.xml 日志配置文件
@ -105,10 +114,11 @@ wcferry-mvn
#### 配置参数 #### 配置参数
本程序内置参数统一前缀wcferry 所有自定义本服务的参数请都放置在此前缀下,如: 本程序内置参数统一前缀wechat.ferry 所有自定义本服务的参数请都放置在此前缀下,如:
```ymal ```ymal
wcferry: wechat:
ferry:
# DLL文件位置 # DLL文件位置
dll-path: /dll/sdk.dll dll-path: /dll/sdk.dll
``` ```
@ -129,7 +139,7 @@ wcferry:
如: 如:
```cmd ```cmd
feat(0): [java]-[wcferry-mvn]-基础类目录划分迁移及代码格式 feat(0): [java]-[wechat-ferry-mvn]-基础类目录划分迁移及代码格式
``` ```
| 名称 | 版本 | | 名称 | 版本 |

View File

@ -11,10 +11,10 @@
<version>2.7.18</version> <version>2.7.18</version>
</parent> </parent>
<artifactId>wcferry-mvn</artifactId> <artifactId>wechat-ferry-mvn</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>wcferry-mvn</name> <name>wechat-ferry-mvn</name>
<description>wcferry客户端Java-Maven版</description> <description>WeChatFerry客户端Java-Maven版</description>
<dependencies> <dependencies>
<dependency> <dependency>
@ -48,6 +48,23 @@
<artifactId>springfox-boot-starter</artifactId> <artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version> <version>3.0.0</version>
</dependency> </dependency>
<!-- Alibaba Fastjson -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.52</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!-- httpclient依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>

View File

@ -1,4 +1,4 @@
package com.iamteer; package com.wechat.ferry;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -10,10 +10,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @date 2024-09-21 12:19 * @date 2024-09-21 12:19
*/ */
@SpringBootApplication @SpringBootApplication
public class WcferryApplication { public class WeChatFerryApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(WcferryApplication.class, args); SpringApplication.run(WeChatFerryApplication.class, args);
} }
} }

View File

@ -1,4 +1,4 @@
package com.iamteer.config; package com.wechat.ferry.config;
import java.util.Collections; import java.util.Collections;

View File

@ -1,4 +1,4 @@
package com.iamteer.config; package com.wechat.ferry.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -26,7 +26,7 @@ public class SwaggerConfig {
public Docket api() { public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select() return new Docket(DocumentationType.SWAGGER_2).select()
// 替换为您的Controller所在的包路径 // 替换为您的Controller所在的包路径
.apis(RequestHandlerSelectors.basePackage("com.iamteer.controller")) .apis(RequestHandlerSelectors.basePackage("com.wechat.ferry.controller"))
// 地址 // 地址
.paths(PathSelectors.any()).build().apiInfo(apiInfo()); .paths(PathSelectors.any()).build().apiInfo(apiInfo());
} }
@ -34,7 +34,7 @@ public class SwaggerConfig {
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder()
// 文档标题 // 文档标题
.title("Wcferry接口文档") .title("WeChatFerry接口文档")
// 文档路径 // 文档路径
.description("微信机器人底层框架接口文档") .description("微信机器人底层框架接口文档")
// 文档版本 // 文档版本

View File

@ -1,45 +1,39 @@
package com.iamteer.runner; package com.wechat.ferry.config;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.ApplicationRunner; import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component; import org.springframework.context.annotation.Configuration;
import com.iamteer.Client; import com.wechat.ferry.handle.WeChatSocketClient;
import com.iamteer.config.WcferryProperties;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* 启动回调-调用微信 * 配置类-注入微信客户端
* *
* @author chandler * @author chandler
* @date 2024-09-21 12:21 * @date 2024-09-30 12:21
*/ */
@Slf4j @Slf4j
@Component @Configuration
public class WechatRunner implements ApplicationRunner { public class WeChatConfiguration {
@Resource @Resource
private WcferryProperties properties; private WeChatFerryProperties properties;
@Override @Resource
public void run(ApplicationArguments args) throws Exception { private ServerProperties serverProperties;
System.out.println(">>>服务启动第一个开始执行的任务<<<<");
runWechat();
}
private void runWechat() { @Bean
log.debug("测试:端口:{},地址:{}", properties.getSocketPort(), properties.getDllPath()); public WeChatSocketClient client() {
log.debug("[读取配置文件]-端口:{},地址:{}", properties.getSocketPort(), properties.getDllPath());
// 连接远程 RPC // 连接远程 RPC
// Client client = new Client("127.0.0.1", 10086); // Client client = new Client("127.0.0.1", 10086);
// 本地启动 RPC // 本地启动 RPC
// Client client = new Client(); // 默认 10086 端口 WeChatSocketClient wechatSocketClient = new WeChatSocketClient(properties.getSocketPort(), properties.getDllPath());
// Client client = new Client(10088,true); // 也可以指定端口
Client client = new Client(properties.getSocketPort(), properties.getDllPath()); // 默认 10086 端口
// 是否已登录 // 是否已登录
// log.info("isLogin: {}", client.isLogin()); // log.info("isLogin: {}", client.isLogin());
@ -54,7 +48,7 @@ public class WechatRunner implements ApplicationRunner {
// client.printContacts(client.getContacts()); // client.printContacts(client.getContacts());
// 获取数据库 // 获取数据库
log.info("dbs: {}", client.getDbNames()); log.info("dbs: {}", wechatSocketClient.getDbNames());
// 获取数据库下的表 // 获取数据库下的表
String db = "MicroMsg.db"; String db = "MicroMsg.db";
@ -77,19 +71,30 @@ public class WechatRunner implements ApplicationRunner {
// 发送表情消息gif 必须要存在 // 发送表情消息gif 必须要存在
// client.sendEmotion("C:\\Projs\\WeChatFerry\\emo.gif", "filehelper"); // client.sendEmotion("C:\\Projs\\WeChatFerry\\emo.gif", "filehelper");
// 使用本机打印
String url = "http://localhost:" + serverProperties.getPort() + "/wechat/msg/receive";
// 接收消息并调用 printWxMsg 处理 // 接收消息并调用 printWxMsg 处理
client.enableRecvMsg(100); wechatSocketClient.enableRecvMsg(100);
Thread thread = new Thread(new Runnable() { Thread thread = new Thread(new Runnable() {
public void run() { public void run() {
while (client.getIsReceivingMsg()) { while (wechatSocketClient.getIsReceivingMsg()) {
client.printWxMsg(client.getMsg()); // 只打印
// wechatSocketClient.printWxMsg(wechatSocketClient.getMsg());
// 转发到boot项目进行消息处理
wechatSocketClient.forwardMsg(wechatSocketClient.getMsg(), url);
} }
} }
}); });
thread.start(); thread.start();
// client.diableRecvMsg(); // 需要停止时调用 // client.diableRecvMsg(); // 需要停止时调用
client.keepRunning(); new Thread(new Runnable() {
public void run() {
wechatSocketClient.keepRunning();
}
}).start();
return wechatSocketClient;
} }
} }

View File

@ -1,4 +1,4 @@
package com.iamteer.config; package com.wechat.ferry.config;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -6,15 +6,15 @@ import org.springframework.stereotype.Component;
import lombok.Data; import lombok.Data;
/** /**
* 配置文件-wcferry的配置文件 * 配置文件-WeChatFerry的配置文件
* *
* @author chandler * @author chandler
* @date 2024-09-21 21:35 * @date 2024-09-21 21:35
*/ */
@Data @Data
@Component @Component
@ConfigurationProperties(prefix = "wcferry") @ConfigurationProperties(prefix = "wechat.ferry")
public class WcferryProperties { public class WeChatFerryProperties {
/** /**
* dll文件位置 * dll文件位置

View File

@ -0,0 +1,44 @@
package com.wechat.ferry.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.ferry.entity.TResponse;
import com.wechat.ferry.enums.ResponseCodeEnum;
import com.wechat.ferry.service.WeChatDllService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
/**
* 控制层-微信DLL处理
*
* @author chandler
* @date 2024-10-01 15:48
*/
@Slf4j
@RestController
@RequestMapping("/wechat/cgi/dll")
@Api(tags = "微信消息处理-接口")
public class WeChatDllController {
private WeChatDllService weChatDllService;
@Autowired
public void setWeChatDllService(WeChatDllService weChatDllService) {
this.weChatDllService = weChatDllService;
}
@ApiOperation(value = "测试", notes = "test")
@PostMapping(value = "/test")
public TResponse<Object> test(@RequestBody JSONObject jsonData) {
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
}

View File

@ -0,0 +1,43 @@
package com.wechat.ferry.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.wechat.ferry.entity.TResponse;
import com.wechat.ferry.enums.ResponseCodeEnum;
import com.wechat.ferry.service.WeChatMsgService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
/**
* 控制层-微信消息处理
*
* @author chandler
* @date 2024-10-01 14:25
*/
@Slf4j
@RestController
@RequestMapping("/wechat/msg")
@Api(tags = "微信消息处理-接口")
public class WeChatMsgController {
private WeChatMsgService weChatMsgService;
@Autowired
public void setWeChatMsgService(WeChatMsgService weChatMsgService) {
this.weChatMsgService = weChatMsgService;
}
@ApiOperation(value = "接收微信消息", notes = "receiveMsg")
@PostMapping(value = "/receive")
public TResponse<Object> receiveMsg(@RequestBody String jsonString) {
weChatMsgService.receiveMsg(jsonString);
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
}

View File

@ -0,0 +1,18 @@
package com.wechat.ferry.entity;
/**
* 返回类接口
*/
public interface IResponse {
/**
* 状态码
*/
String getCode();
/**
* 返回信息
*/
String getMsg();
}

View File

@ -0,0 +1,111 @@
package com.wechat.ferry.entity;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.wechat.ferry.enums.ResponseCodeEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Accessors;
/**
* 返回类封装
*/
@Data
@ToString
@Accessors(chain = true)
public class TResponse<T> {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 状态码
*/
@ApiModelProperty(value = "状态码")
private String code;
/**
* 返回信息
*/
@ApiModelProperty(value = "返回信息")
private String msg;
/**
* 响应时间
*/
@ApiModelProperty(value = "响应时间")
private String time;
/**
* 响应数据
*/
@ApiModelProperty(value = "响应数据")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private T data;
/**
* 返回类
*
* @author chandler
* @date 2023/4/5 11:31
* @param t 返回码类
* @param data 返回数据
* @return TResponse对象
*/
public TResponse(IResponse t, T data) {
this(t);
this.data = data;
}
/**
* 返回类
*
* @author chandler
* @date 2023/4/5 11:31
* @param t 返回码类
* @param msg 返回信息
* @return TResponse对象
*/
public TResponse(IResponse t, String msg) {
this.code = t.getCode();
this.msg = msg;
this.time = LocalDateTime.now().format(FORMATTER);
}
/**
* 返回类
*
* @author chandler
* @date 2023/4/5 11:31
* @param t 返回码类
* @param msg 返回信息
* @return TResponse对象
*/
public TResponse(IResponse t, T data, String msg) {
this(t, data);
// 重写返回信息-替换默认的信息
this.msg = msg;
}
public TResponse(IResponse t) {
this.code = t.getCode();
this.msg = t.getMsg();
this.time = LocalDateTime.now().format(FORMATTER);
}
public static <T> TResponse<T> ok(IResponse t) {
return new TResponse<>(t);
}
public static <T> TResponse<T> ok(IResponse t, T data) {
return new TResponse<>(t, data);
}
public static <T> TResponse<T> fail(String msg) {
return new TResponse<>(ResponseCodeEnum.FAILED, msg);
}
}

View File

@ -0,0 +1,87 @@
package com.wechat.ferry.entity.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* DTO-微信消息
*
* @author chandler
* @date 2024-09-26 19:56
*/
@Data
public class WxMsgDTO {
/**
* 是否自己发送的
*/
@ApiModelProperty(value = "是否自己发送的")
private Boolean isSelf;
/**
* 是否群消息
*/
@ApiModelProperty(value = "是否群消息")
private Boolean isGroup;
/**
* 消息id
*/
@ApiModelProperty(value = "消息id")
private Long id;
/**
* 消息类型
*/
@ApiModelProperty(value = "消息类型")
private Integer type;
/**
* 消息类型
*/
@ApiModelProperty(value = "消息类型")
private Integer ts;
/**
* 群id如果是群消息的话
*/
@ApiModelProperty(value = "群id如果是群消息的话")
private String roomId;
/**
* 消息内容
*/
@ApiModelProperty(value = "消息内容")
private String content;
/**
* 消息发送者
*/
@ApiModelProperty(value = "消息发送者")
private String sender;
/**
* 签名
*/
@ApiModelProperty(value = "签名")
private String sign;
/**
* 缩略图
*/
@ApiModelProperty(value = "缩略图")
private String thumb;
/**
* 附加内容
*/
@ApiModelProperty(value = "附加内容")
private String extra;
/**
* 消息xml
*/
@ApiModelProperty(value = "消息xml")
private String xml;
}

View File

@ -0,0 +1,65 @@
package com.wechat.ferry.enums;
import com.wechat.ferry.entity.IResponse;
/**
* 枚举-返回类状态码
*/
public enum ResponseCodeEnum implements IResponse {
/**
* 成功-200
*/
SUCCESS("200", "请求成功"),
/**
* 参数错误-400
*/
PARAM_ERROR("400", "参数错误"),
/**
* 401-身份验证失败
*/
NO_AUTH("401", "身份验证失败"),
/**
* 403-您无权访问此资源
*/
UNAUTHORIZED("403", "您无权访问此资源"),
/**
* 404-未找到该资源
*/
NOT_FOUND("404", "未找到该资源"),
/**
* 失败-500
*/
FAILED("500", "请求失败"),
;
private final String code;
private final String msg;
ResponseCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
@Override
public String toString() {
return this.name() + "{" + code + '|' + msg + "}";
}
}

View File

@ -0,0 +1,42 @@
package com.wechat.ferry.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-性别
*
* @author chandler
* @date 2024/10/01 15:42
*/
@Getter
@AllArgsConstructor
public enum SexEnum {
/**
* 0-未知
*/
UNKNOWN("0", "未知"),
/**
* 1-
*/
BOY("1", ""),
/**
* 2-
*/
GIRL("2", ""),
/**
* 未匹配上
*/
UN_MATCH("", null),
// 结束
;
private final String code;
private final String name;
}

View File

@ -0,0 +1,32 @@
package com.wechat.ferry.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-消息类型
*
* @author chandler
* @date 2024/10/01 15:55
*/
@Getter
@AllArgsConstructor
public enum WeChatMsgTypeEnum {
/**
* 0-未知
*/
UNKNOWN("0", "未知"),
/**
* 未匹配上
*/
UN_MATCH("", null),
// 结束
;
private final String code;
private final String name;
}

View File

@ -1,4 +1,4 @@
package com.iamteer; package com.wechat.ferry.handle;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
@ -8,66 +8,90 @@ import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import com.iamteer.service.SDK; import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iamteer.entity.Wcf;
import com.iamteer.entity.Wcf.DbQuery;
import com.iamteer.entity.Wcf.DbRow;
import com.iamteer.entity.Wcf.DbTable;
import com.iamteer.entity.Wcf.DecPath;
import com.iamteer.entity.Wcf.Functions;
import com.iamteer.entity.Wcf.MemberMgmt;
import com.iamteer.entity.Wcf.Request;
import com.iamteer.entity.Wcf.Response;
import com.iamteer.entity.Wcf.RpcContact;
import com.iamteer.entity.Wcf.UserInfo;
import com.iamteer.entity.Wcf.Verification;
import com.iamteer.entity.Wcf.WxMsg;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.wechat.ferry.entity.dto.WxMsgDTO;
import com.wechat.ferry.entity.po.Wcf;
import com.wechat.ferry.entity.po.Wcf.DbQuery;
import com.wechat.ferry.entity.po.Wcf.DbRow;
import com.wechat.ferry.entity.po.Wcf.DbTable;
import com.wechat.ferry.entity.po.Wcf.DecPath;
import com.wechat.ferry.entity.po.Wcf.Functions;
import com.wechat.ferry.entity.po.Wcf.MemberMgmt;
import com.wechat.ferry.entity.po.Wcf.Request;
import com.wechat.ferry.entity.po.Wcf.Response;
import com.wechat.ferry.entity.po.Wcf.RpcContact;
import com.wechat.ferry.entity.po.Wcf.UserInfo;
import com.wechat.ferry.entity.po.Wcf.Verification;
import com.wechat.ferry.entity.po.Wcf.WxMsg;
import com.wechat.ferry.enums.SexEnum;
import com.wechat.ferry.service.SDK;
import com.wechat.ferry.utils.HttpClientUtil;
import io.sisu.nng.Socket; import io.sisu.nng.Socket;
import io.sisu.nng.pair.Pair1Socket; import io.sisu.nng.pair.Pair1Socket;
import lombok.extern.slf4j.Slf4j;
public class Client { /**
* 处理层-微信客户端
*
* @author Changhua
* @date 2023-12-06 22:11
*/
@Slf4j
public class WeChatSocketClient {
/**
* 消息缓冲区大小16M
*/
private static final Integer BUFFER_SIZE = 16 * 1024 * 1024;
/**
* 默认IP
*/
private static final String DEFAULT_HOST = "127.0.0.1";
/**
* 请求地址
*/
private static final String CMD_URL = "tcp://%s:%s";
private static final Logger logger = LoggerFactory.getLogger(Client.class);
private static final int BUFFER_SIZE = 16 * 1024 * 1024; // 16M
private Socket cmdSocket = null; private Socket cmdSocket = null;
private Socket msgSocket = null; private Socket msgSocket = null;
private static String DEFAULT_HOST = "127.0.0.1";
private static int PORT = 10086; /**
private static String CMDURL = "tcp://%s:%s"; * 是否收到消息
private static String DEFAULT_DLL_PATH = System.getProperty("user.dir") + "\\dll\\sdk.dll"; */
private boolean isReceivingMsg = false; private boolean isReceivingMsg = false;
/**
* 是否为本地端口
*/
private boolean isLocalHostPort = false; private boolean isLocalHostPort = false;
/**
* 消息返回
*/
private BlockingQueue<WxMsg> msgQ; private BlockingQueue<WxMsg> msgQ;
private String host; private final String host;
private int port; private final Integer port;
private String dllPath;
public Client() { public WeChatSocketClient(Integer port, String dllPath) {
this(DEFAULT_HOST, PORT, false, DEFAULT_DLL_PATH);
}
public Client(int port, String dllPath) {
this(DEFAULT_HOST, port, false, dllPath); this(DEFAULT_HOST, port, false, dllPath);
} }
public Client(String host, int port, boolean debug, String dllPath) { public WeChatSocketClient(String host, Integer port, boolean debug, String dllPath) {
this.host = host; this.host = host;
this.port = port; this.port = port;
this.dllPath = dllPath;
SDK INSTANCE = Native.load(dllPath, SDK.class); SDK INSTANCE = Native.load(dllPath, SDK.class);
int status = INSTANCE.WxInitSDK(debug, port); int status = INSTANCE.WxInitSDK(debug, port);
if (status != 0) { if (status != 0) {
logger.error("启动 RPC 失败: {}", status); log.error("启动 RPC 失败: {}", status);
System.exit(-1); System.exit(-1);
} }
connectRPC(String.format(CMDURL, host, port), INSTANCE); connectRPC(String.format(CMD_URL, host, port), INSTANCE);
if (DEFAULT_HOST.equals(host) || "localhost".equalsIgnoreCase(host)) { if (DEFAULT_HOST.equals(host) || "localhost".equalsIgnoreCase(host)) {
isLocalHostPort = true; isLocalHostPort = true;
} }
@ -77,16 +101,16 @@ public class Client {
try { try {
cmdSocket = new Pair1Socket(); cmdSocket = new Pair1Socket();
cmdSocket.dial(url); cmdSocket.dial(url);
// logger.info("请点击登录微信"); while (!isLogin()) {
while (!isLogin()) { // 直到登录成功 // 直到登录成功
waitMs(1000); waitMs(1000);
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("连接 RPC 失败: ", e); log.error("连接 RPC 失败: ", e);
System.exit(-1); System.exit(-1);
} }
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> {
logger.info("关闭..."); log.info("关闭...");
diableRecvMsg(); diableRecvMsg();
if (isLocalHostPort) { if (isLocalHostPort) {
INSTANCE.WxDestroySDK(); INSTANCE.WxDestroySDK();
@ -102,7 +126,7 @@ public class Client {
long size = cmdSocket.receive(ret, true); long size = cmdSocket.receive(ret, true);
return Response.parseFrom(Arrays.copyOfRange(ret.array(), 0, (int)size)); return Response.parseFrom(Arrays.copyOfRange(ret.array(), 0, (int)size));
} catch (Exception e) { } catch (Exception e) {
logger.error("命令调用失败: ", e); log.error("命令调用失败: ", e);
return null; return null;
} }
} }
@ -110,7 +134,7 @@ public class Client {
/** /**
* 当前微信客户端是否登录微信号 * 当前微信客户端是否登录微信号
* *
* @return * @return 是否登录结果
*/ */
public boolean isLogin() { public boolean isLogin() {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_IS_LOGIN_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_IS_LOGIN_VALUE).build();
@ -124,22 +148,21 @@ public class Client {
/** /**
* 获得微信客户端登录的微信ID * 获得微信客户端登录的微信ID
* *
* @return * @return 微信ID
*/ */
public String getSelfWxid() { public String getSelfWxId() {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_SELF_WXID_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_SELF_WXID_VALUE).build();
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
if (rsp != null) { if (rsp != null) {
return rsp.getStr(); return rsp.getStr();
} }
return ""; return "";
} }
/** /**
* 获取所有消息类型 * 获取所有消息类型
* *
* @return * @return 消息类型集合
*/ */
public Map<Integer, String> getMsgTypes() { public Map<Integer, String> getMsgTypes() {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_MSG_TYPES_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_MSG_TYPES_VALUE).build();
@ -147,7 +170,6 @@ public class Client {
if (rsp != null) { if (rsp != null) {
return rsp.getTypes().getTypesMap(); return rsp.getTypes().getTypesMap();
} }
return Wcf.MsgTypes.newBuilder().build().getTypesMap(); return Wcf.MsgTypes.newBuilder().build().getTypesMap();
} }
@ -159,7 +181,7 @@ public class Client {
* "filehelper": "文件传输助手", * "filehelper": "文件传输助手",
* "newsapp": "新闻", * "newsapp": "新闻",
* *
* @return * @return 联系人列表
*/ */
public List<RpcContact> getContacts() { public List<RpcContact> getContacts() {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_CONTACTS_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_CONTACTS_VALUE).build();
@ -167,7 +189,6 @@ public class Client {
if (rsp != null) { if (rsp != null) {
return rsp.getContacts().getContactsList(); return rsp.getContacts().getContactsList();
} }
return Wcf.RpcContacts.newBuilder().build().getContactsList(); return Wcf.RpcContacts.newBuilder().build().getContactsList();
} }
@ -176,7 +197,7 @@ public class Client {
* *
* @param db 数据库名 * @param db 数据库名
* @param sql 执行的sql语句 * @param sql 执行的sql语句
* @return * @return 数据记录列表
*/ */
public List<DbRow> querySql(String db, String sql) { public List<DbRow> querySql(String db, String sql) {
DbQuery dbQuery = DbQuery.newBuilder().setSql(sql).setDb(db).build(); DbQuery dbQuery = DbQuery.newBuilder().setSql(sql).setDb(db).build();
@ -191,7 +212,7 @@ public class Client {
/** /**
* 获取所有数据库名 * 获取所有数据库名
* *
* @return * @return 数据库名称列表
*/ */
public List<String> getDbNames() { public List<String> getDbNames() {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_NAMES_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_NAMES_VALUE).build();
@ -199,15 +220,14 @@ public class Client {
if (rsp != null) { if (rsp != null) {
return rsp.getDbs().getNamesList(); return rsp.getDbs().getNamesList();
} }
return Wcf.DbNames.newBuilder().build().getNamesList(); return Wcf.DbNames.newBuilder().build().getNamesList();
} }
/** /**
* 获取指定数据库中的所有表 * 获取指定数据库中的所有表
* *
* @param db * @param db 数据库名称
* @return * @return 数据库中表列表
*/ */
public Map<String, String> getDbTables(String db) { public Map<String, String> getDbTables(String db) {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_TABLES_VALUE).setStr(db).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_GET_DB_TABLES_VALUE).setStr(db).build();
@ -218,7 +238,6 @@ public class Client {
tables.put(tbl.getName(), tbl.getSql()); tables.put(tbl.getName(), tbl.getSql());
} }
} }
return tables; return tables;
} }
@ -233,17 +252,16 @@ public class Client {
* @author Changhua * @author Changhua
* @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ", * @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ",
* "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2"); * "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2");
**/ */
public int sendText(String msg, String receiver, String aters) { public int sendText(String msg, String receiver, String aters) {
Wcf.TextMsg textMsg = Wcf.TextMsg.newBuilder().setMsg(msg).setReceiver(receiver).setAters(aters).build(); Wcf.TextMsg textMsg = Wcf.TextMsg.newBuilder().setMsg(msg).setReceiver(receiver).setAters(aters).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_TXT_VALUE).setTxt(textMsg).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_TXT_VALUE).setTxt(textMsg).build();
logger.debug("sendText: {}", bytesToHex(req.toByteArray())); log.debug("sendText: {}", bytesToHex(req.toByteArray()));
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
int ret = -1; int ret = -1;
if (rsp != null) { if (rsp != null) {
ret = rsp.getStatus(); ret = rsp.getStatus();
} }
return ret; return ret;
} }
@ -257,13 +275,12 @@ public class Client {
public int sendImage(String path, String receiver) { public int sendImage(String path, String receiver) {
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build(); Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_IMG_VALUE).setFile(pathMsg).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_IMG_VALUE).setFile(pathMsg).build();
logger.debug("sendImage: {}", bytesToHex(req.toByteArray())); log.debug("sendImage: {}", bytesToHex(req.toByteArray()));
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
int ret = -1; int ret = -1;
if (rsp != null) { if (rsp != null) {
ret = rsp.getStatus(); ret = rsp.getStatus();
} }
return ret; return ret;
} }
@ -277,13 +294,12 @@ public class Client {
public int sendFile(String path, String receiver) { public int sendFile(String path, String receiver) {
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build(); Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_FILE_VALUE).setFile(pathMsg).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_FILE_VALUE).setFile(pathMsg).build();
logger.debug("sendFile: {}", bytesToHex(req.toByteArray())); log.debug("sendFile: {}", bytesToHex(req.toByteArray()));
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
int ret = -1; int ret = -1;
if (rsp != null) { if (rsp != null) {
ret = rsp.getStatus(); ret = rsp.getStatus();
} }
return ret; return ret;
} }
@ -292,20 +308,19 @@ public class Client {
* *
* @param receiver 接收者微信id * @param receiver 接收者微信id
* @param xml xml内容 * @param xml xml内容
* @param path * @param path 路径
* @param type * @param type 类型
* @return 发送结果状态码 * @return 发送结果状态码
*/ */
public int sendXml(String receiver, String xml, String path, int type) { public int sendXml(String receiver, String xml, String path, int type) {
Wcf.XmlMsg xmlMsg = Wcf.XmlMsg.newBuilder().setContent(xml).setReceiver(receiver).setPath(path).setType(type).build(); Wcf.XmlMsg xmlMsg = Wcf.XmlMsg.newBuilder().setContent(xml).setReceiver(receiver).setPath(path).setType(type).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_XML_VALUE).setXml(xmlMsg).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_XML_VALUE).setXml(xmlMsg).build();
logger.debug("sendXml: {}", bytesToHex(req.toByteArray())); log.debug("sendXml: {}", bytesToHex(req.toByteArray()));
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
int ret = -1; int ret = -1;
if (rsp != null) { if (rsp != null) {
ret = rsp.getStatus(); ret = rsp.getStatus();
} }
return ret; return ret;
} }
@ -319,13 +334,12 @@ public class Client {
public int sendEmotion(String path, String receiver) { public int sendEmotion(String path, String receiver) {
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build(); Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(path).setReceiver(receiver).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_EMOTION_VALUE).setFile(pathMsg).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_SEND_EMOTION_VALUE).setFile(pathMsg).build();
logger.debug("sendEmotion: {}", bytesToHex(req.toByteArray())); log.debug("sendEmotion: {}", bytesToHex(req.toByteArray()));
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
int ret = -1; int ret = -1;
if (rsp != null) { if (rsp != null) {
ret = rsp.getStatus(); ret = rsp.getStatus();
} }
return ret; return ret;
} }
@ -413,12 +427,12 @@ public class Client {
/** /**
* 判断是否是艾特自己的消息 * 判断是否是艾特自己的消息
* *
* @param wxMsgXml * @param wxMsgXml XML消息
* @param wxMsgContent * @param wxMsgContent 消息内容
* @return * @return 是否
*/ */
public boolean isAtMeMsg(String wxMsgXml, String wxMsgContent) { public boolean isAtMeMsg(String wxMsgXml, String wxMsgContent) {
String format = String.format("<atuserlist><![CDATA[%s]]></atuserlist>", getSelfWxid()); String format = String.format("<atuserlist><![CDATA[%s]]></atuserlist>", getSelfWxId());
boolean isAtAll = wxMsgContent.startsWith("@所有人") || wxMsgContent.startsWith("@all"); boolean isAtAll = wxMsgContent.startsWith("@所有人") || wxMsgContent.startsWith("@all");
if (wxMsgXml.contains(format) && !isAtAll) { if (wxMsgXml.contains(format) && !isAtAll) {
return true; return true;
@ -430,9 +444,10 @@ public class Client {
try { try {
msgSocket = new Pair1Socket(); msgSocket = new Pair1Socket();
msgSocket.dial(url); msgSocket.dial(url);
msgSocket.setReceiveTimeout(2000); // 2 秒超时 // 设置 2 秒超时
msgSocket.setReceiveTimeout(2000);
} catch (Exception e) { } catch (Exception e) {
logger.error("创建消息 RPC 失败", e); log.error("创建消息 RPC 失败", e);
return; return;
} }
ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE); ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);
@ -448,7 +463,7 @@ public class Client {
try { try {
msgSocket.close(); msgSocket.close();
} catch (Exception e) { } catch (Exception e) {
logger.error("关闭连接失败", e); log.error("关闭连接失败", e);
} }
} }
@ -460,7 +475,7 @@ public class Client {
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ENABLE_RECV_TXT_VALUE).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ENABLE_RECV_TXT_VALUE).build();
Response rsp = sendCmd(req); Response rsp = sendCmd(req);
if (rsp == null) { if (rsp == null) {
logger.error("启动消息接收失败"); log.error("启动消息接收失败");
isReceivingMsg = false; isReceivingMsg = false;
return; return;
} }
@ -501,21 +516,34 @@ public class Client {
for (RpcContact c : contacts) { for (RpcContact c : contacts) {
int value = c.getGender(); int value = c.getGender();
String gender; String gender;
if (value == 1) { if (SexEnum.BOY.getCode().equals(String.valueOf(value))) {
gender = ""; gender = "";
} else if (value == 2) { } else if (SexEnum.GIRL.getCode().equals(String.valueOf(value))) {
gender = ""; gender = "";
} else { } else {
gender = "未知"; gender = "未知";
} }
log.info("{}, {}, {}, {}, {}, {}, {}", c.getWxid(), c.getName(), c.getCode(), c.getCountry(), c.getProvince(), c.getCity(), gender);
logger.info("{}, {}, {}, {}, {}, {}, {}", c.getWxid(), c.getName(), c.getCode(), c.getCountry(), c.getProvince(), c.getCity(), gender);
} }
} }
public void printWxMsg(WxMsg msg) { public void printWxMsg(WxMsg msg) {
logger.info("{}[{}]:{}:{}:{}\n{}", msg.getSender(), msg.getRoomid(), msg.getId(), msg.getType(), WxMsgDTO dto = new WxMsgDTO();
msg.getXml().replace("\n", "").replace("\t", ""), msg.getContent()); dto.setIsSelf(msg.getIsSelf());
dto.setIsGroup(msg.getIsGroup());
dto.setId(msg.getId());
dto.setType(msg.getType());
dto.setTs(msg.getTs());
dto.setRoomId(msg.getRoomid());
dto.setContent(msg.getContent());
dto.setSender(msg.getSender());
dto.setSign(msg.getSign());
dto.setThumb(msg.getThumb());
dto.setExtra(msg.getExtra());
dto.setXml(msg.getXml().replace("\n", "").replace("\t", ""));
String jsonString = JSONObject.toJSONString(dto);
log.info("收到消息: {}", jsonString);
} }
private String bytesToHex(byte[] bytes) { private String bytesToHex(byte[] bytes) {
@ -532,4 +560,30 @@ public class Client {
} }
} }
public void forwardMsg(WxMsg msg, String url) {
WxMsgDTO dto = new WxMsgDTO();
dto.setIsSelf(msg.getIsSelf());
dto.setIsGroup(msg.getIsGroup());
dto.setId(msg.getId());
dto.setType(msg.getType());
dto.setTs(msg.getTs());
dto.setRoomId(msg.getRoomid());
dto.setContent(msg.getContent());
dto.setSender(msg.getSender());
dto.setSign(msg.getSign());
dto.setThumb(msg.getThumb());
dto.setExtra(msg.getExtra());
dto.setXml(msg.getXml().replace("\n", "").replace("\t", ""));
String jsonString = JSONObject.toJSONString(dto);
try {
String responseStr = HttpClientUtil.doPostJson(url, jsonString);
if (!JSONObject.parseObject(responseStr).getString("code").equals("200")) {
log.error("本机消息转发失败!-URL{}", url);
}
} catch (Exception e) {
log.error("转发接口报错:", e);
}
}
} }

View File

@ -1,29 +1,28 @@
package com.iamteer.service; package com.wechat.ferry.service;
import com.sun.jna.Library; import com.sun.jna.Library;
/** /**
* SDK.dll的接口类 * SDK.dll的接口类
* *
* @Description * @author xinggq
* @Author xinggq * @date 2024-07-10 15:21
* @Date 2024/7/10
*/ */
public interface SDK extends Library { public interface SDK extends Library {
/** /**
* 初始化SDK * 初始化SDK
* *
* @param debug * @param debug 开发模式
* @param port * @param port 端口
* @return * @return 状态值
*/ */
int WxInitSDK(boolean debug, int port); int WxInitSDK(boolean debug, int port);
/** /**
* 退出SDK * 退出SDK
* *
* @return * @return 状态值
*/ */
int WxDestroySDK(); int WxDestroySDK();

View File

@ -0,0 +1,11 @@
package com.wechat.ferry.service;
/**
* 业务接口-对接原本DLL的接口
*
* @author chandler
* @date 2024-10-01 15:57
*/
public interface WeChatDllService {
}

View File

@ -0,0 +1,21 @@
package com.wechat.ferry.service;
/**
* 业务接口-消息处理
*
* @author chandler
* @date 2024-10-01 14:30
*/
public interface WeChatMsgService {
/**
* 接收消息
*
* @param jsonString json数据
*
* @author chandler
* @date 2024-10-01 14:33
*/
void receiveMsg(String jsonString);
}

View File

@ -0,0 +1,3 @@
# Ignore everything in this directory
*
# Except this file !.gitkeep

View File

@ -0,0 +1,19 @@
package com.wechat.ferry.service.impl;
import org.springframework.stereotype.Service;
import com.wechat.ferry.service.WeChatDllService;
import lombok.extern.slf4j.Slf4j;
/**
* 业务实现层-对接原本DLL的接口
*
* @author chandler
* @date 2024-10-01 15:58
*/
@Slf4j
@Service
public class WeChatDllServiceImpl implements WeChatDllService {
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.service.impl;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSON;
import com.wechat.ferry.entity.dto.WxMsgDTO;
import com.wechat.ferry.service.WeChatMsgService;
import lombok.extern.slf4j.Slf4j;
/**
* 业务实现层-消息处理
*
* @author chandler
* @date 2024-10-01 14:35
*/
@Slf4j
@Service
public class WeChatMsgServiceImpl implements WeChatMsgService {
@Override
public void receiveMsg(String jsonString) {
// 转为JSON对象
WxMsgDTO dto = JSON.parseObject(jsonString, WxMsgDTO.class);
// TODO 这里可以拓展自己需要的功能
log.debug("[收到消息]-[消息内容]-打印:{}", dto);
}
}

View File

@ -0,0 +1,169 @@
package com.wechat.ferry.utils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import lombok.extern.slf4j.Slf4j;
/**
* HTTP请求类
*/
@Slf4j
@SuppressWarnings("all")
public class HttpClientUtil {
/**
* 带参数的get请求
*
* @param url
* @param param
* @return String
*/
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* 不带参数的get请求
*
* @param url
* @return String
*/
public static String doGet(String url) {
return doGet(url, null);
}
/**
* 带参数的post请求
*
* @param url
* @param param
* @return String
*/
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* 不带参数的post请求
*
* @param url
* @return String
*/
public static String doPost(String url) {
return doPost(url, null);
}
/**
* 传送json类型的post请求
*
* @param url
* @param json
* @return String
*/
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}

View File

@ -0,0 +1,130 @@
package com.wechat.ferry.utils;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class XmlJsonConvertUtil {
public static String readFile(String path) {
String str = "";
try {
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocate(new Long(file.length()).intValue());
// fc向buffer中读入数据
fc.read(bb);
bb.flip();
str = new String(bb.array(), "UTF8");
fc.close();
fis.close();
} catch (Exception e) {
log.error("异常:{} ", e.getMessage());
}
return str;
}
/**
* xml转json
*
* @param xmlStr
* @return
*/
public static JSONObject xml2Json(String xmlStr) {
JSONObject json = new JSONObject();
try {
xmlStr = xmlStr.replace("<?xml version=\\\"1.0\\\"?>\\n", "");
Document doc = DocumentHelper.parseText(xmlStr);
dom4j2Json(doc.getRootElement(), json);
} catch (DocumentException e) {
log.error("异常:{} ", e.getMessage());
}
return json;
}
/**
* xml转json
*
* @param element
* @param json
*/
public static void dom4j2Json(Element element, JSONObject json) {
// 如果是属性
for (Object o : element.attributes()) {
Attribute attr = (Attribute)o;
if (!isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), attr.getValue());
}
}
List<Element> chdEl = element.elements();
if (chdEl.isEmpty() && !isEmpty(element.getText())) {
// 如果没有子元素,只有一个值
json.put(element.getName(), element.getText());
}
for (Element e : chdEl) {
// 有子元素
if (!e.elements().isEmpty()) {
// 子元素也有子元素
JSONObject chdjson = new JSONObject();
dom4j2Json(e, chdjson);
Object o = json.get(e.getName());
if (o != null) {
JSONArray jsona = null;
if (o instanceof JSONObject) {
// 如果此元素已存在,则转为jsonArray
JSONObject jsono = (JSONObject)o;
json.remove(e.getName());
jsona = new JSONArray();
jsona.add(jsono);
jsona.add(chdjson);
}
if (o instanceof JSONArray) {
jsona = (JSONArray)o;
jsona.add(chdjson);
}
json.put(e.getName(), jsona);
} else {
if (!chdjson.isEmpty()) {
json.put(e.getName(), chdjson);
}
}
} else {
// 子元素没有子元素
for (Object o : element.attributes()) {
Attribute attr = (Attribute)o;
if (!isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), attr.getValue());
}
}
if (!e.getText().isEmpty()) {
json.put(e.getName(), e.getText());
}
}
}
}
public static boolean isEmpty(String str) {
if (str == null || str.trim().isEmpty() || "null".equals(str)) {
return true;
}
return false;
}
}

View File

@ -9,15 +9,16 @@ spring:
# 配置应用信息 # 配置应用信息
application: application:
# 应用名 # 应用名
name: wcferry-mvn name: wechat-ferry
# swagger适配 # swagger适配
mvc: mvc:
pathmatch: pathmatch:
matching-strategy: ant_path_matcher matching-strategy: ant_path_matcher
# 本服务参数 # 本服务参数
wcferry: wechat:
ferry:
# DLL文件位置 # DLL文件位置
dll-path: E:\WeChatFerry\clients\java\wcferry-mvn\dll\sdk.dll dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll
# socket端口 # socket端口
socket-port: 10086 socket-port: 10086

View File

@ -83,6 +83,13 @@
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO"> <logger name="org.springframework.boot.autoconfigure.logging" level="INFO">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</logger> </logger>
<!-- httpclient 屏蔽 -->
<logger name="org.apache" level="OFF">
<appender-ref ref="error"/>
</logger>
<logger name="httpclient" level="OFF">
<appender-ref ref="error"/>
</logger>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 --> <!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="DEBUG"> <root level="DEBUG">

View File

@ -0,0 +1,3 @@
# Ignore everything in this directory
*
# Except this file !.gitkeep

View File

@ -1,7 +1,7 @@
syntax = "proto3"; syntax = "proto3";
package wcf; package wcf;
option java_package = "com.iamteer.entity"; option java_package = "com.wechat.ferry.entity.po";
enum Functions { enum Functions {
FUNC_RESERVED = 0x00; FUNC_RESERVED = 0x00;

View File

@ -0,0 +1,3 @@
# Ignore everything in this directory
*
# Except this file !.gitkeep