feat(0): [java]-[wechat-ferry-mvn]-基础功能完善

1.引入JSON格式化依赖
2.编写XML和JSON转换工具类
3.消息接口支持boot接口打印,便于后续boot项目自身接管处理
4.项目名称及目录统一调整
5.支持接收消息后转发消息
6.打包配置修改
This commit is contained in:
chandler 2024-10-05 12:54:43 +08:00
parent b97d899f02
commit 06cee96313
40 changed files with 5924 additions and 1961 deletions

View File

@ -9,11 +9,12 @@
### 环境准备
| 名称 | 版本 |
|-------|-----------|
| JDK | 1.8+ |
| Maven | 3.8+ |
| 微信 | 3.9.10.27 |
| 名称 | 版本 | 备注 |
|-------|-----------|----|
| JDK | 1.8+ | √ |
| Maven | 3.8+ | √ |
| 微信 | 3.9.10.27 | √ |
| MySQL | 8.0+ | 备用 |
### 下载文件
@ -49,6 +50,10 @@ wechat:
socket-port: 10086
```
### 数据库修改-目前未使用
为了更好的拓展应用本客户端后续将采用mysql数据库请自行安装mysql数据库
### 编译运行
找到 src/main/java/com/wechat/ferry/WeChatFerryApplication.java 类
@ -71,6 +76,9 @@ swagger地址http://localhost:9201/swagger-ui/index.html
| protobuf-java | 3.22.2 | rpc |
| jna | 5.6.0 | 态访问系统本地库 |
| nng-java | 1.4.0 | 本地包 |
| fastjson2 | 2.0.52 | 序列化 |
| dom4j | 2.1.3 | XML解析包 |
| httpclient | 4.5.13 | 客户端请求 |
### 模块结构
@ -86,7 +94,8 @@ wechat-ferry-mvn
│ │ │ ├─config 配置
│ │ │ ├─entity 聚合模型
│ │ │ │ ├─dto DTO模型
│ │ │ │ ├─po 实体模型
│ │ │ │ ├─po 数据库实体(与表结构一一对应,否则请使用DTO)
│ │ │ │ ├─proto PB实体
│ │ │ │ └─vo 视图层返回体目录
│ │ │ ├─enums 枚举
│ │ │ ├─handle 处理层

View File

@ -14,8 +14,14 @@
<artifactId>wechat-ferry-mvn</artifactId>
<packaging>jar</packaging>
<name>wechat-ferry-mvn</name>
<version>39.2.4</version>
<description>WeChatFerry客户端Java-Maven版</description>
<properties>
<application.main.class>com.wechat.ferry.WeChatFerryApplication</application.main.class>
<app.name>wechat-ferry-mvn</app.name>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -30,6 +36,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
@ -77,6 +88,35 @@
<version>5.6.0</version>
</dependency>
<!-- 配置文件加解密 -->
<!-- <dependency>-->
<!-- <groupId>com.github.ulisesbocchio</groupId>-->
<!-- <artifactId>jasypt-spring-boot-starter</artifactId>-->
<!-- <version>3.0.5</version>-->
<!-- </dependency>-->
<!-- orm 相关 -->
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>mybatis-plus-annotation</artifactId>-->
<!-- <version>3.5.7</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>mybatis-plus-extension</artifactId>-->
<!-- <version>3.5.7</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.baomidou</groupId>-->
<!-- <artifactId>mybatis-plus-boot-starter</artifactId>-->
<!-- <version>3.5.7</version>-->
<!-- </dependency>-->
<!-- mysql数据库驱动 -->
<!-- <dependency>-->
<!-- <groupId>com.mysql</groupId>-->
<!-- <artifactId>mysql-connector-j</artifactId>-->
<!-- <version>8.0.33</version>-->
<!-- </dependency>-->
<!-- 引入本地lib -->
<dependency>
<groupId>io.sisu.nng</groupId>
@ -90,6 +130,19 @@
</dependencies>
<build>
<!-- 最终 Jar 包的名字 -->
<finalName>${app.name}-${project.version}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 排除特定资源 -->
<excludes>
<exclude>bin/*</exclude>
<exclude>proto/*</exclude>
<exclude>sql/*</exclude>
</excludes>
</resource>
</resources>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
@ -98,8 +151,25 @@
</extension>
</extensions>
<!-- 同时添加maven插件用于编译protobuf生成java文件 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<!-- 主类入口 -->
<mainClass>${application.main.class}</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 同时添加maven插件用于编译protobuf生成java文件 -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
@ -125,17 +195,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<excludes>
<exclude>**/*.proto</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

View File

@ -0,0 +1,28 @@
package com.wechat.ferry.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* 配置类-配置错误页面转发首页
*
* @author chandler
* @date 2024-10-04 10:21
*/
@Slf4j
@Configuration
@Component
public class ErrorPageConfig implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html");
registry.addErrorPages(error404Page);
}
}

View File

@ -27,7 +27,7 @@ public class WeChatConfiguration {
private ServerProperties serverProperties;
@Bean
public WeChatSocketClient client() {
public WeChatSocketClient weChatSocketClient() {
log.debug("[读取配置文件]-端口:{},地址:{}", properties.getSocketPort(), properties.getDllPath());
// 连接远程 RPC
// Client client = new Client("127.0.0.1", 10086);

View File

@ -1,5 +1,7 @@
package com.wechat.ferry.config;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ -26,4 +28,39 @@ public class WeChatFerryProperties {
*/
private Integer socketPort;
/**
* 需要开启消息处理的群
*/
private List<String> openMsgGroups;
/**
* 接收消息转发开关
*/
private Boolean receiveMsgFwdSwitch = false;
/**
* 接收消息转发URL
*/
private List<String> receiveMsgFwdUrls;
/**
* 发送消息前转发开关
*/
private Boolean sendMsgFrontFwdSwitch = false;
/**
* 发送消息前转发URL
*/
private List<String> sendMsgFrontFwdUrls;
/**
* 发送消息后转发开关
*/
private Boolean sendMsgBackFwdSwitch = false;
/**
* 发送消息后转发URL
*/
private List<String> sendMsgBackFwdUrls;
}

View File

@ -0,0 +1,33 @@
package com.wechat.ferry.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 配置类-配置项目首页
*
* @author chandler
* @date 2024-10-04 10:30
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 将根路径 "/" 的请求重定向到 "/index.html"
registry.addViewController("/").setViewName("forward:/index.html");
WebMvcConfigurer.super.addViewControllers(registry);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 添加资源处理器用于映射静态资源路径
registry.addResourceHandler("/**")
// 添加资路径
.addResourceLocations("classpath:/templates/");
WebMvcConfigurer.super.addResourceHandlers(registry);
}
}

View File

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

View File

@ -1,13 +1,32 @@
package com.wechat.ferry.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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.entity.vo.request.WxPpDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpSendCardMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendTextMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpSendCardMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendTextMsgResp;
import com.wechat.ferry.enums.ResponseCodeEnum;
import com.wechat.ferry.service.WeChatDllService;
@ -23,7 +42,7 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
@RestController
@RequestMapping("/wechat/cgi/dll")
@RequestMapping("/wechat/cgi/wcf")
@Api(tags = "微信消息处理-接口")
public class WeChatDllController {
@ -34,11 +53,181 @@ public class WeChatDllController {
this.weChatDllService = weChatDllService;
}
@ApiOperation(value = "测试", notes = "test")
@PostMapping(value = "/test")
public TResponse<Object> test(@RequestBody JSONObject jsonData) {
@ApiOperation(value = "查询登录状态", notes = "loginStatus")
@PostMapping(value = "/loginStatus")
public TResponse<Object> loginStatus() {
Boolean status = weChatDllService.loginStatus();
return TResponse.ok(ResponseCodeEnum.SUCCESS, status);
}
@ApiOperation(value = "获取登录微信号", notes = "queryLoginWeChatNo")
@PostMapping(value = "/loginWeChatNo")
public TResponse<Object> queryLoginWeChatNo() {
String weChatNo = weChatDllService.queryLoginWeChatId();
return TResponse.ok(ResponseCodeEnum.SUCCESS, weChatNo);
}
@ApiOperation(value = "获取登录微信信息", notes = "queryLoginWeChatInfo")
@PostMapping(value = "/loginWeChatInfo")
public TResponse<Object> queryLoginWeChatInfo() {
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "获取消息类型", notes = "queryMsgTypeList")
@PostMapping(value = "/list/msgType")
public TResponse<List<WxPpMsgTypeResp>> queryMsgTypeList() {
List<WxPpMsgTypeResp> list = weChatDllService.queryMsgTypeList();
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取联系人", notes = "queryContactsList")
@PostMapping(value = "/list/contacts")
public TResponse<List<WxPpContactsResp>> queryContactsList() {
List<WxPpContactsResp> list = weChatDllService.queryContactsList();
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取可查询数据库", notes = "queryDatabaseSql")
@PostMapping(value = "/list/dbSql")
public TResponse<List<WxPpDatabaseRowResp>> queryDatabaseSql(@Validated @RequestBody WxPpDatabaseSqlReq request) {
List<WxPpDatabaseRowResp> list = weChatDllService.queryDatabaseSql(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取数据库所有表名称", notes = "queryDatabaseAllTableName")
@PostMapping(value = "/list/dbTableName")
public TResponse<List<String>> queryDatabaseAllTableName() {
List<String> list = weChatDllService.queryDatabaseAllTableName();
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取指定数据库中的表", notes = "queryDatabaseTable")
@PostMapping(value = "/list/dbTable")
public TResponse<List<String>> queryDatabaseTable(@Validated @RequestBody WxPpDatabaseTableReq request) {
List<String> list = weChatDllService.queryDatabaseTable(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
// @ApiOperation(value = "获取语音消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/voiceMsg")
// public TResponse<Object> queryVoiceMsg() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
@ApiOperation(value = "查询群成员", notes = "queryGroupMember")
@PostMapping(value = "/list/groupMember")
public TResponse<List<WxPpGroupMemberResp>> queryGroupMember(@Validated @RequestBody WxPpGroupMemberReq request) {
List<WxPpGroupMemberResp> list = weChatDllService.queryGroupMember(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "发送消息汇总入口", notes = "sendMsgMaster")
@PostMapping(value = "/send/msgMaster")
public TResponse<WxPpSendTextMsgResp> sendMsgMaster(@Validated @RequestBody String jsonString) {
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "发送文本消息(可 @", notes = "sendTextMsg")
@PostMapping(value = "/send/textMsg")
public TResponse<WxPpSendTextMsgResp> sendTextMsg(@Validated @RequestBody WxPpSendTextMsgReq request) {
WxPpSendTextMsgResp resp = weChatDllService.sendTextMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送图片消息", notes = "sendImageMsg")
@PostMapping(value = "/send/imageMsg")
public TResponse<WxPpSendImageMsgResp> sendImageMsg(@Validated @RequestBody WxPpSendImageMsgReq request) {
WxPpSendImageMsgResp resp = weChatDllService.sendImageMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送文件消息", notes = "sendFileMsg")
@PostMapping(value = "/send/fileMsg")
public TResponse<WxPpSendFileMsgResp> sendFileMsg(@Validated @RequestBody WxPpSendFileMsgReq request) {
WxPpSendFileMsgResp resp = weChatDllService.sendFileMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送卡片消息", notes = "sendCardMsg")
@PostMapping(value = "/send/cardMsg")
public TResponse<WxPpSendCardMsgResp> sendCardMsg(@Validated @RequestBody WxPpSendCardMsgReq request) {
WxPpSendCardMsgResp resp = weChatDllService.sendCardMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送表情消息", notes = "sendEmojiMsg")
@PostMapping(value = "/send/emojiMsg")
public TResponse<WxPpSendEmojiMsgResp> sendEmojiMsg(@Validated @RequestBody WxPpSendEmojiMsgReq request) {
WxPpSendEmojiMsgResp resp = weChatDllService.sendEmojiMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
// @ApiOperation(value = "拍一拍群友", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "转发消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "开启接收消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "关闭接收消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "查询数据库", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "获取朋友圈消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "下载图片、视频、文件", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "解密图片", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "添加群成员", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "删除群成员", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "邀请群成员", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
}

View File

@ -4,13 +4,13 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* DTO-微信消息
* DTO-ge微信消息
*
* @author chandler
* @date 2024-09-26 19:56
*/
@Data
public class WxMsgDTO {
public class WxPpMsgDTO {
/**
* 是否自己发送的

View File

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

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-查询-个微数据库SQL查询
*
* @author chandler
* @date 2024-10-02 17:10
*/
@Data
@ApiModel(value = "wxPpDatabaseSqlReq", description = "个微数据库SQL查询请求入参")
public class WxPpDatabaseSqlReq {
/**
* 数据库名称
*/
@ApiModelProperty(value = "数据库名称")
private String databaseName;
/**
* SQL语句
*/
@ApiModelProperty(value = "SQL语句")
private String sqlText;
}

View File

@ -0,0 +1,23 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-查询-个微数据库表查询
*
* @author chandler
* @date 2024-10-02 17:55
*/
@Data
@ApiModel(value = "wxPpDatabaseTableReq", description = "个微数据库表查询请求入参")
public class WxPpDatabaseTableReq {
/**
* 数据库名称
*/
@ApiModelProperty(value = "数据库名称")
private String databaseName;
}

View File

@ -0,0 +1,23 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微查询群成员
*
* @author chandler
* @date 2024-10-02 20:55
*/
@Data
@ApiModel(value = "wxPpGroupMemberReq", description = "个微查询群成员请求入参")
public class WxPpGroupMemberReq {
/**
* 群编号
*/
@ApiModelProperty(value = "群编号")
private String groupNo;
}

View File

@ -0,0 +1,43 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微发送卡片消息
*
* @author chandler
* @date 2024-10-04 23:11
*/
@Data
@ApiModel(value = "wxPpSendCardMsgReq", description = "个微发送卡片消息请求入参")
public class WxPpSendCardMsgReq {
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
/**
* XML报文
*/
@ApiModelProperty(value = "XML报文")
private String xml;
/**
* 路径
*/
@ApiModelProperty(value = "路径")
private String path;
/**
* 类型
*/
@ApiModelProperty(value = "类型")
private Integer type;
}

View File

@ -0,0 +1,31 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微发送表情消息
*
* @author chandler
* @date 2024-10-04 23:14
*/
@Data
@ApiModel(value = "wxPpSendEmojiMsgReq", description = "个微发送表情消息请求入参")
public class WxPpSendEmojiMsgReq {
/**
* 路径
*/
@ApiModelProperty(value = "路径")
private String path;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

View File

@ -0,0 +1,31 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微发送文件消息
*
* @author chandler
* @date 2024-10-04 23:08
*/
@Data
@ApiModel(value = "wxPpSendFileMsgReq", description = "个微发送文件消息请求入参")
public class WxPpSendFileMsgReq {
/**
* 图片地址
*/
@ApiModelProperty(value = "图片地址")
private String path;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

View File

@ -0,0 +1,31 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微发送图片消息
*
* @author chandler
* @date 2024-10-04 15:55
*/
@Data
@ApiModel(value = "wxPpSendImageMsgReq", description = "个微发送图片消息请求入参")
public class WxPpSendImageMsgReq {
/**
* 图片地址
*/
@ApiModelProperty(value = "图片地址")
private String path;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

View File

@ -0,0 +1,49 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 请求入参-个微发送文本消息
*
* @author chandler
* @date 2024-10-02 20:33
*/
@Data
@ApiModel(value = "wxPpSendTextMsgReq", description = "个微发送文本消息请求入参")
public class WxPpSendTextMsgReq {
/**
* 消息文本
* 消息内容如果是 @ 消息则需要有跟 @ 的人数量相同的 @
*/
@ApiModelProperty(value = "消息文本")
private String msgText;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
/**
* 要艾特的用户
* 群聊时要 @ 的人私聊时为空字符串多个用逗号分隔
* 艾特所有人用 notify@all必须是群主或者管理员才有权限
*/
@ApiModelProperty(value = "要艾特的用户")
private List<String> atUsers;
/**
* 是否艾特全体
* 默认为false
*/
@ApiModelProperty(value = "是否艾特全体")
private Boolean isAtAll = false;
}

View File

@ -0,0 +1,101 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微联系人
*
* @author chandler
* @date 2024/10/02 17:01
*/
@Data
@ApiModel(value = "wxPpContactsResp", description = "个微联系人查询请求出参")
public class WxPpContactsResp {
/**
* 微信编号
*/
@ApiModelProperty(value = "微信编号")
private String weChatNo;
/**
* 微信代码
*/
@ApiModelProperty(value = "微信代码")
private String weChatCode;
/**
* 备注
*/
@ApiModelProperty(value = "备注")
private String remark;
/**
* 微信昵称
*/
@ApiModelProperty(value = "微信昵称")
private String nickName;
/**
* 国家
*/
@ApiModelProperty(value = "国家")
private String country;
/**
* 国家拼音
*/
@ApiModelProperty(value = "国家拼音")
private String countryPinyin;
/**
* /
*/
@ApiModelProperty(value = "省/州")
private String province;
/**
* /州拼音
*/
@ApiModelProperty(value = "省/州拼音")
private String provincePinyin;
/**
* 城市
*/
@ApiModelProperty(value = "城市")
private String city;
/**
* 城市拼音
*/
@ApiModelProperty(value = "城市拼音")
private String cityPinyin;
/**
* 性别
*/
@ApiModelProperty(value = "性别")
private String sex;
/**
* 性别-翻译
*/
@ApiModelProperty(value = "性别-翻译")
private String sexLabel;
/**
* 类型
*/
@ApiModelProperty(value = "类型")
private String type;
/**
* 类型-翻译
*/
@ApiModelProperty(value = "类型-翻译")
private String typeLabel;
}

View File

@ -0,0 +1,35 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微数据库字段
*
* @author chandler
* @date 2024/10/02 17:15
*/
@Data
@ApiModel(value = "wxPpDatabaseFieldResp", description = "个微数据库字段查询请求出参")
public class WxPpDatabaseFieldResp {
/**
* 字段类型
*/
@ApiModelProperty(value = "字段类型")
private String type;
/**
* 字段
*/
@ApiModelProperty(value = "字段")
private String column;
/**
* 字段值
*/
@ApiModelProperty(value = "字段值")
private String value;
}

View File

@ -0,0 +1,25 @@
package com.wechat.ferry.entity.vo.response;
import java.util.List;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微数据库记录
*
* @author chandler
* @date 2024/10/02 17:14
*/
@Data
@ApiModel(value = "wxPpDatabaseRowResp", description = "个微数据库记录查询请求出参")
public class WxPpDatabaseRowResp {
/**
* 字段列表
*/
@ApiModelProperty(value = "字段列表")
private List<WxPpDatabaseFieldResp> fieldList;
}

View File

@ -0,0 +1,35 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微查询群成员
*
* @author chandler
* @date 2024/10/01 21:26
*/
@Data
@ApiModel(value = "wxPpGroupMemberResp", description = "个微群成员查询请求出参")
public class WxPpGroupMemberResp {
/**
* 微信编号
*/
@ApiModelProperty(value = "微信编号")
private String weChatNo;
/**
* 微信昵称
*/
@ApiModelProperty(value = "微信昵称")
private String nickName;
/**
* 状态
*/
@ApiModelProperty(value = "状态")
private String state;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微消息类型
*
* @author chandler
* @date 2024/10/01 21:26
*/
@Data
@ApiModel(value = "wxPpMsgTypeResp", description = "个微消息类型查询请求出参")
public class WxPpMsgTypeResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微发送卡片消息
*
* @author chandler
* @date 2024/10/04 23:11
*/
@Data
@ApiModel(value = "wxPpSendCardMsgResp", description = "个微发送卡片消息请求出参")
public class WxPpSendCardMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微发送GIF消息
*
* @author chandler
* @date 2024/10/04 23:13
*/
@Data
@ApiModel(value = "wxPpSendEmojiMsgResp", description = "个微发送GIF消息请求出参")
public class WxPpSendEmojiMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微发送文件消息
*
* @author chandler
* @date 2024/10/04 23:07
*/
@Data
@ApiModel(value = "wxPpSendFileMsgResp", description = "个微发送文件消息请求出参")
public class WxPpSendFileMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微发送图像消息
*
* @author chandler
* @date 2024/10/03 10:17
*/
@Data
@ApiModel(value = "wxPpSendImageMsgResp", description = "个微发送图像消息请求出参")
public class WxPpSendImageMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,29 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微发送文本消息
*
* @author chandler
* @date 2024/10/03 10:17
*/
@Data
@ApiModel(value = "wxPpSendTextMsgResp", description = "个微发送文本消息请求出参")
public class WxPpSendTextMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -1,5 +1,9 @@
package com.wechat.ferry.enums;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -39,4 +43,16 @@ public enum SexEnum {
private final String code;
private final String name;
/**
* map集合 keycode val枚举
*/
public static final Map<String, SexEnum> codeMap = Arrays.stream(values()).collect(Collectors.toMap(SexEnum::getCode, v -> v));
/**
* 根据code获取枚举
*/
public static SexEnum getCodeMap(String code) {
return codeMap.getOrDefault(code, UN_MATCH);
}
}

View File

@ -0,0 +1,192 @@
package com.wechat.ferry.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-WCF消息类型
*
* @author chandler
* @date 2024/10/01 15:55
*/
@Getter
@AllArgsConstructor
public enum WcfMsgTypeEnum {
/**
* 0-朋友圈消息
*/
FRIEND_CIRCLE_MSG("0", "朋友圈消息"),
/**
* 1-文字
*/
TEXT("1", "文字"),
/**
* 3-图片
*/
PICTURE("3", "图片"),
/**
* 34-语音
*/
VOICE("34", "语音"),
/**
* 37-好友确认
*/
FRIEND_CONFIRM("37", "好友确认"),
/**
* 40-可能认识的朋友消息-POSSIBLEFRIEND_MSG
*/
POSSIBLE_FRIEND_MSG("40", "可能认识的朋友"),
/**
* 42-名片
*/
VISITING_CARD("42", "名片"),
/**
* 43-视频
*/
VIDEO("43", "视频"),
/**
* 47-石头剪刀布 | 表情图片
*/
EMOJI_IMAGE("47", "石头剪刀布 | 表情图片"),
/**
* 48-位置
*/
POSITION("48", "位置"),
/**
* 49-共享实时位置文件转账链接
*/
SHARE("49", "共享实时位置、文件、转账、链接"),
/**
* 50-语音电话消息-VOIPMSG
*/
VOIP_MSG("50", "语音电话消息"),
/**
* 51-微信初始化
*/
WECHAT_INIT("51", "微信初始化"),
/**
* 52-语音电话通知-VOIPNOTIFY
*/
VOIP_NOTIFY("52", "语音电话通知"),
/**
* 53-语音电话邀请-VOIPINVITE
*/
VOIP_INVITE("53", "语音电话邀请"),
/**
* 62-小视频
*/
SMALL_VIDEO("62", "小视频"),
/**
* 66-微信红包
*/
WECHAT_RED_ENVELOPE("66", "微信红包"),
/**
* 9999-系统通知-SYSNOTICE
*/
SYS_NOTICE("9999", "系统通知"),
/**
* 10000-红包系统消息
*/
RED_ENVELOPE_SYS_NOTICE("10000", "红包、系统消息"),
/**
* 10002-撤回消息
*/
WITHDRAW_MSG("10002", "撤回消息"),
/**
* 1048625-搜狗表情
*/
SO_GOU_EMOJI("1048625", "搜狗表情"),
/**
* 16777265-链接
*/
LINK("16777265", "链接"),
/**
* 436207665-微信红包
*/
RED_ENVELOPE("436207665", "微信红包"),
/**
* 536936497-红包封面
*/
RED_ENVELOPE_COVER("536936497", "红包封面"),
/**
* 754974769-视频号视频
*/
VIDEO_NUMBER_VIDEO("754974769", "视频号视频"),
/**
* 771751985-视频号名片
*/
VIDEO_NUMBER_CARD("771751985", "视频号名片"),
/**
* 822083633-引用消息
*/
QUOTE_MSG("822083633", "引用消息"),
/**
* 922746929-拍一拍
*/
PAT_ONE_PAT("922746929", "拍一拍"),
/**
* 973078577-视频号直播
*/
VIDEO_NUMBER_LIVE("973078577", "视频号直播"),
/**
* 974127153-商品链接
*/
PRODUCT_LINK("974127153", "商品链接"),
/**
* 975175729-视频号直播-TODO
*/
UNKNOWN("975175729", "视频号直播"),
/**
* 1040187441-音乐链接
*/
MUSIC_LINK("1040187441", "音乐链接"),
/**
* 1090519089-文件
*/
FILE("1090519089", "文件"),
/**
* 未匹配上
*/
UN_MATCH("", null),
// 结束
;
private final String code;
private final String name;
}

View File

@ -0,0 +1,39 @@
package com.wechat.ferry.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-是否
*
* @author chandler
* @date 2023/3/14 10:21
*/
@Getter
@AllArgsConstructor
public enum WhetherEnum {
/**
* 1-Y-
*/
YES("1", "", "Y", true),
/**
* 2-N-
*/
NO("2", "", "N", false),
/**
* 未匹配上
*/
UN_MATCH("", null, null, null),
// 结束
;
private final String code;
private final String name;
private final String key;
private final Boolean bool;
}

View File

@ -0,0 +1,65 @@
package com.wechat.ferry.enums;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-微信联系人类型
*
* @author chandler
* @date 2024/10/02 18:35
*/
@Getter
@AllArgsConstructor
public enum WxContactsTypeEnum {
/**
* 1-个微
*/
PERSON("1", "个微", ""),
/**
* 2-企微
*/
WORK("2", "企微", "@openim"),
/**
* 3-群组
*/
GROUP("3", "群组", "@chatroom"),
/**
* 4-公众号
*/
OFFICIAL_ACCOUNT("4", "公众号", "gh_"),
/**
* 未匹配上
*/
UN_MATCH("", null, null),
// 结束
;
private final String code;
private final String name;
private final String affix;
/**
* map集合 keycode val枚举
*/
public static final Map<String, WxContactsTypeEnum> codeMap =
Arrays.stream(values()).collect(Collectors.toMap(WxContactsTypeEnum::getCode, v -> v));
/**
* 根据code获取枚举
*/
public static WxContactsTypeEnum getCodeMap(String code) {
return codeMap.getOrDefault(code, UN_MATCH);
}
}

View File

@ -10,20 +10,20 @@ import java.util.concurrent.BlockingQueue;
import com.alibaba.fastjson2.JSONObject;
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.entity.dto.WxPpMsgDTO;
import com.wechat.ferry.entity.proto.Wcf;
import com.wechat.ferry.entity.proto.Wcf.DbQuery;
import com.wechat.ferry.entity.proto.Wcf.DbRow;
import com.wechat.ferry.entity.proto.Wcf.DbTable;
import com.wechat.ferry.entity.proto.Wcf.DecPath;
import com.wechat.ferry.entity.proto.Wcf.Functions;
import com.wechat.ferry.entity.proto.Wcf.MemberMgmt;
import com.wechat.ferry.entity.proto.Wcf.Request;
import com.wechat.ferry.entity.proto.Wcf.Response;
import com.wechat.ferry.entity.proto.Wcf.RpcContact;
import com.wechat.ferry.entity.proto.Wcf.UserInfo;
import com.wechat.ferry.entity.proto.Wcf.Verification;
import com.wechat.ferry.entity.proto.Wcf.WxMsg;
import com.wechat.ferry.enums.SexEnum;
import com.wechat.ferry.service.SDK;
import com.wechat.ferry.utils.HttpClientUtil;
@ -118,7 +118,7 @@ public class WeChatSocketClient {
}));
}
private Response sendCmd(Request req) {
public Response sendCmd(Request req) {
try {
ByteBuffer bb = ByteBuffer.wrap(req.toByteArray());
cmdSocket.send(bb);
@ -528,7 +528,7 @@ public class WeChatSocketClient {
}
public void printWxMsg(WxMsg msg) {
WxMsgDTO dto = new WxMsgDTO();
WxPpMsgDTO dto = new WxPpMsgDTO();
dto.setIsSelf(msg.getIsSelf());
dto.setIsGroup(msg.getIsGroup());
dto.setId(msg.getId());
@ -561,19 +561,24 @@ public class WeChatSocketClient {
}
public void forwardMsg(WxMsg msg, String url) {
WxMsgDTO dto = new WxMsgDTO();
String xml = msg.getXml();
xml = xml.replaceAll(">[\\s\\p{Zs}]*<", "><");
String content = msg.getContent();
content = content.replaceAll(">[\\s\\p{Zs}]*<", "><");
WxPpMsgDTO dto = new WxPpMsgDTO();
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.setContent(content);
dto.setSender(msg.getSender());
dto.setSign(msg.getSign());
dto.setThumb(msg.getThumb());
dto.setExtra(msg.getExtra());
dto.setXml(msg.getXml().replace("\n", "").replace("\t", ""));
dto.setXml(xml);
String jsonString = JSONObject.toJSONString(dto);
try {

View File

@ -1,5 +1,25 @@
package com.wechat.ferry.service;
import java.util.List;
import com.wechat.ferry.entity.vo.request.WxPpDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpSendCardMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendTextMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpSendCardMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendTextMsgResp;
/**
* 业务接口-对接原本DLL的接口
*
@ -8,4 +28,144 @@ package com.wechat.ferry.service;
*/
public interface WeChatDllService {
/**
* 当前微信客户端是否登录微信号
*
* @return true-已登录 false-未登录
*
* @author chandler
* @date 2024-10-01 21:20
*/
Boolean loginStatus();
/**
* 获得微信客户端登录的微信ID
*
* @return 微信ID
*
* @author chandler
* @date 2024-10-01 21:22
*/
String queryLoginWeChatId();
/**
* 获取所有消息类型
*
* @return 消息类型列表
*
* @author chandler
* @date 2024-10-01 21:22
*/
List<WxPpMsgTypeResp> queryMsgTypeList();
/**
* 获取所有联系人
*
* @return 联系人列表
*
* @author chandler
* @date 2024-10-02 16:59
*/
List<WxPpContactsResp> queryContactsList();
/**
* 获取可查询数据库
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 17:52
*/
List<WxPpDatabaseRowResp> queryDatabaseSql(WxPpDatabaseSqlReq request);
/**
* 获取数据库所有表名称
*
* @return 数据库名称列表
*
* @author chandler
* @date 2024-10-02 17:53
*/
List<String> queryDatabaseAllTableName();
/**
* 获取数据库表
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 17:52
*/
List<String> queryDatabaseTable(WxPpDatabaseTableReq request);
/**
* 查询群成员
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 20:59
*/
List<WxPpGroupMemberResp> queryGroupMember(WxPpGroupMemberReq request);
/**
* 发送文本消息 @
*
* @param request 请求入参
* @return 消息发送返回
*
* @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ", "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2");
*
* @author chandler
* @date 2024-10-02 20:40
*/
WxPpSendTextMsgResp sendTextMsg(WxPpSendTextMsgReq request);
/**
* 发送图片消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:06
*/
WxPpSendImageMsgResp sendImageMsg(WxPpSendImageMsgReq request);
/**
* 发送文件消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:15
*/
WxPpSendFileMsgResp sendFileMsg(WxPpSendFileMsgReq request);
/**
* 发送卡片消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:15
*/
WxPpSendCardMsgResp sendCardMsg(WxPpSendCardMsgReq request);
/**
* 发送表情消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:29
*/
WxPpSendEmojiMsgResp sendEmojiMsg(WxPpSendEmojiMsgReq request);
}

View File

@ -1,7 +1,43 @@
package com.wechat.ferry.service.impl;
import org.springframework.stereotype.Service;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.wechat.ferry.entity.proto.Wcf;
import com.wechat.ferry.entity.vo.request.WxPpDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpSendCardMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpSendTextMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpDatabaseFieldResp;
import com.wechat.ferry.entity.vo.response.WxPpDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpSendCardMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpSendTextMsgResp;
import com.wechat.ferry.enums.SexEnum;
import com.wechat.ferry.enums.WxContactsTypeEnum;
import com.wechat.ferry.handle.WeChatSocketClient;
import com.wechat.ferry.service.WeChatDllService;
import lombok.extern.slf4j.Slf4j;
@ -16,4 +52,302 @@ import lombok.extern.slf4j.Slf4j;
@Service
public class WeChatDllServiceImpl implements WeChatDllService {
private WeChatSocketClient wechatSocketClient;
@Autowired
public void setWechatSocketClient(WeChatSocketClient wechatSocketClient) {
this.wechatSocketClient = wechatSocketClient;
}
@Override
public Boolean loginStatus() {
Boolean status = wechatSocketClient.isLogin();
log.info("[查询]-[登录状态]-status:{}", status);
return status;
}
@Override
public String queryLoginWeChatId() {
String weChatNo = wechatSocketClient.getSelfWxId();
log.info("[查询]-[登录微信编号]-weChatNo:{}", weChatNo);
return weChatNo;
}
@Override
public List<WxPpMsgTypeResp> queryMsgTypeList() {
List<WxPpMsgTypeResp> list = new ArrayList<>();
Map<Integer, String> msgTypeMap = wechatSocketClient.getMsgTypes();
if (!CollectionUtils.isEmpty(msgTypeMap)) {
WxPpMsgTypeResp resp;
for (Map.Entry<Integer, String> entry : msgTypeMap.entrySet()) {
resp = new WxPpMsgTypeResp();
resp.setId(entry.getKey());
resp.setName(entry.getValue());
list.add(resp);
}
}
log.info("[查询]-[所消息类型]-共查到:{}条", list.size());
return list;
}
@Override
public List<WxPpContactsResp> queryContactsList() {
List<WxPpContactsResp> list = new ArrayList<>();
List<Wcf.RpcContact> rpcContactList = wechatSocketClient.getContacts();
if (!CollectionUtils.isEmpty(rpcContactList)) {
for (Wcf.RpcContact rpcContact : rpcContactList) {
WxPpContactsResp vo = new WxPpContactsResp();
vo.setWeChatNo(rpcContact.getWxid());
vo.setWeChatCode(rpcContact.getCode());
vo.setRemark(rpcContact.getRemark());
vo.setNickName(rpcContact.getName());
if (!ObjectUtils.isEmpty(rpcContact.getCountry())) {
vo.setCountryPinyin(rpcContact.getCountry());
if ("CN".equals(rpcContact.getCountry())) {
vo.setCountry("中国");
}
}
if (!ObjectUtils.isEmpty(rpcContact.getProvince())) {
vo.setProvincePinyin(rpcContact.getProvince());
vo.setProvince(null);
}
if (!ObjectUtils.isEmpty(rpcContact.getCity())) {
vo.setCityPinyin(rpcContact.getCity());
vo.setCity(null);
}
// 性别处理
if (!ObjectUtils.isEmpty(rpcContact.getGender())) {
vo.setSex(SexEnum.getCodeMap(String.valueOf(rpcContact.getGender())).getCode());
vo.setSexLabel(SexEnum.getCodeMap(String.valueOf(rpcContact.getGender())).getName());
}
// 是否为企业微信
if (!ObjectUtils.isEmpty(rpcContact.getWxid())) {
if (rpcContact.getWxid().endsWith(WxContactsTypeEnum.WORK.getAffix())) {
vo.setType(WxContactsTypeEnum.WORK.getCode());
vo.setTypeLabel(WxContactsTypeEnum.WORK.getName());
} else if (rpcContact.getWxid().endsWith(WxContactsTypeEnum.GROUP.getAffix())) {
vo.setType(WxContactsTypeEnum.GROUP.getCode());
vo.setTypeLabel(WxContactsTypeEnum.GROUP.getName());
} else if (rpcContact.getWxid().startsWith(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getAffix())) {
vo.setType(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getCode());
vo.setTypeLabel(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getName());
} else {
vo.setType(WxContactsTypeEnum.PERSON.getCode());
vo.setTypeLabel(WxContactsTypeEnum.PERSON.getName());
}
}
list.add(vo);
}
}
log.info("[查询]-[联系人]-共查到:{}条", list.size());
return list;
}
@Override
public List<WxPpDatabaseRowResp> queryDatabaseSql(WxPpDatabaseSqlReq request) {
List<WxPpDatabaseRowResp> list = new ArrayList<>();
List<Wcf.DbRow> wcfList = wechatSocketClient.querySql(request.getDatabaseName(), request.getSqlText());
if (!CollectionUtils.isEmpty(wcfList)) {
// List<Map<String, Object>> result = new ArrayList<>();
// // 获取指定的行
// for (Wcf.DbRow row : wcfList) {
// Map<String, Object> rowMap = new HashMap<>();
// // 遍历每一列
// for (Wcf.DbField field : row.getFieldsList()) {
// ByteString content = field.getContent();
// String column = field.getColumn();
// int type = field.getType();
// rowMap.put(column, converterSqlVal(type, content));
// }
// result.add(rowMap);
// }
for (Wcf.DbRow dbRow : wcfList) {
WxPpDatabaseRowResp rowVo = new WxPpDatabaseRowResp();
List<WxPpDatabaseFieldResp> fieldVoList = new ArrayList<>();
for (Wcf.DbField dbField : dbRow.getFieldsList()) {
WxPpDatabaseFieldResp fieldVo = new WxPpDatabaseFieldResp();
fieldVo.setType(String.valueOf(dbField.getType()));
fieldVo.setColumn(dbField.getColumn());
String value = (String)converterSqlVal(dbField.getType(), dbField.getContent());
fieldVo.setValue(value);
fieldVoList.add(fieldVo);
}
rowVo.setFieldList(fieldVoList);
list.add(rowVo);
}
}
log.info("[查询]-[联系人]-wcfList:{}", wcfList);
return list;
}
@Override
public List<String> queryDatabaseAllTableName() {
List<String> list = wechatSocketClient.getDbNames();
log.info("[查询]-[数据库名称列表]-共查到:{}条", list.size());
return list;
}
@Override
public List<String> queryDatabaseTable(WxPpDatabaseTableReq request) {
log.info("[查询]-[查询表]-request:{}", request);
Map<String, String> wcfMap = wechatSocketClient.getDbTables(request.getDatabaseName());
log.info("[查询]-[查询表]-查到:{}", wcfMap);
return Collections.emptyList();
}
@Override
public List<WxPpGroupMemberResp> queryGroupMember(WxPpGroupMemberReq request) {
List<WxPpGroupMemberResp> list = new ArrayList<>();
// 查询群成员
List<Wcf.DbRow> wcfList = new ArrayList<>();
if (!ObjectUtils.isEmpty(request.getGroupNo())) {
wcfList =
wechatSocketClient.querySql("MicroMsg.db", "SELECT RoomData FROM ChatRoom WHERE ChatRoomName = '" + request.getGroupNo() + "';");
}
// 查询联系人
List<Wcf.DbRow> dbList = wechatSocketClient.querySql("MicroMsg.db", "SELECT UserName, NickName, Type FROM Contact;");
Map<String, String> dbMap = new HashMap<>();
if (!CollectionUtils.isEmpty(dbList)) {
for (Wcf.DbRow dbRow : dbList) {
List<Wcf.DbField> dbFieldList = dbRow.getFieldsList();
if (!ObjectUtils.isEmpty(dbFieldList)) {
WxPpGroupMemberResp vo = new WxPpGroupMemberResp();
for (Wcf.DbField dbField : dbFieldList) {
if ("UserName".equals(dbField.getColumn())) {
vo = new WxPpGroupMemberResp();
String content = (String)converterSqlVal(dbField.getType(), dbField.getContent());
vo.setWeChatNo(content);
}
if ("NickName".equals(dbField.getColumn())) {
String content = (String)converterSqlVal(dbField.getType(), dbField.getContent());
vo.setNickName(content);
dbMap.put(vo.getWeChatNo(), vo.getNickName());
}
}
}
}
}
// 查询群名称
if (!CollectionUtils.isEmpty(wcfList)) {
for (Wcf.DbRow dbRow : wcfList) {
List<Wcf.DbField> dbFieldList = dbRow.getFieldsList();
if (!ObjectUtils.isEmpty(dbFieldList)) {
WxPpGroupMemberResp vo = new WxPpGroupMemberResp();
for (Wcf.DbField dbField : dbFieldList) {
if ("RoomData".equals(dbField.getColumn())) {
try {
byte[] roomDataBytes = dbField.getContent().toByteArray();
Wcf.RoomData roomData = Wcf.RoomData.parseFrom(roomDataBytes);
for (Wcf.RoomData.RoomMember member : roomData.getMembersList()) {
vo = new WxPpGroupMemberResp();
vo.setWeChatNo(member.getWxid());
String nickName = member.getName();
if (ObjectUtils.isEmpty(nickName)) {
// 如果没有设置群昵称则默认为微信名称
nickName = dbMap.get(member.getWxid());
}
vo.setNickName(nickName);
vo.setState(String.valueOf(member.getState()));
list.add(vo);
}
} catch (InvalidProtocolBufferException e) {
log.error("异常:", e);
}
}
}
}
}
}
log.info("[查询]-[查询群成员]-共查到:{}条", list.size());
return list;
}
@Override
public WxPpSendTextMsgResp sendTextMsg(WxPpSendTextMsgReq request) {
log.info("[发送消息]-[文本消息]-入参打印:{}", request);
String atUser = "";
if (request.getIsAtAll()) {
// 艾特全体仅管理员有效
atUser = "@all";
} else {
// 处理艾特的人员
if (!CollectionUtils.isEmpty(request.getAtUsers())) {
atUser = String.join(",", request.getAtUsers());
}
}
int state = wechatSocketClient.sendText(request.getMsgText(), request.getRecipient(), atUser);
log.info("[发送消息]-[文本消息]-处理结束");
return null;
}
@Override
public WxPpSendImageMsgResp sendImageMsg(WxPpSendImageMsgReq request) {
int state = wechatSocketClient.sendImage(request.getPath(), request.getRecipient());
return null;
}
@Override
public WxPpSendFileMsgResp sendFileMsg(WxPpSendFileMsgReq request) {
int state = wechatSocketClient.sendFile(request.getPath(), request.getRecipient());
return null;
}
@Override
public WxPpSendCardMsgResp sendCardMsg(WxPpSendCardMsgReq request) {
int state = wechatSocketClient.sendXml(request.getRecipient(), request.getXml(), request.getPath(), request.getType());
return null;
}
@Override
public WxPpSendEmojiMsgResp sendEmojiMsg(WxPpSendEmojiMsgReq request) {
int state = wechatSocketClient.sendEmotion(request.getPath(), request.getRecipient());
return null;
}
public Function<byte[], Object> getSqlType(int type) {
Map<Integer, Function<byte[], Object>> sqlTypeMap = new HashMap<>();
// 初始化SQL_TYPES 根据类型执行不同的Func
sqlTypeMap.put(1, bytes -> ByteBuffer.wrap(bytes).getInt());
sqlTypeMap.put(2, bytes -> ByteBuffer.wrap(bytes).getFloat());
sqlTypeMap.put(3, bytes -> new String(bytes, StandardCharsets.UTF_8));
sqlTypeMap.put(4, bytes -> bytes);
sqlTypeMap.put(5, bytes -> null);
return sqlTypeMap.get(type);
}
public Object converterSqlVal(int type, ByteString content) {
// 根据每一列的类型转换
Function<byte[], Object> converter = getSqlType(type);
if (converter != null) {
return converter.apply(content.toByteArray());
} else {
log.warn("未知的SQL类型: {}", type);
return content.toByteArray();
}
}
/**
* 转换艾特用户
*
* @param groupNo 群组编号
* @param atUsers 艾特的用户(名称/微信编号)
* @return 组装后的艾特用户
*
* @author chandler
* @date 2024-10-03 11:35
*/
public String dealAtUser(String groupNo, List<String> atUsers) {
String atUserStr = "";
if (!CollectionUtils.isEmpty(atUsers)) {
// 取出要艾特的用户
for (String atUser : atUsers) {
}
}
return atUserStr;
}
}

View File

@ -1,10 +1,19 @@
package com.wechat.ferry.service.impl;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.alibaba.fastjson2.JSON;
import com.wechat.ferry.entity.dto.WxMsgDTO;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.entity.dto.WxPpMsgDTO;
import com.wechat.ferry.service.WeChatDllService;
import com.wechat.ferry.service.WeChatMsgService;
import com.wechat.ferry.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
@ -18,12 +27,66 @@ import lombok.extern.slf4j.Slf4j;
@Service
public class WeChatMsgServiceImpl implements WeChatMsgService {
private WeChatDllService weChatDllService;
@Autowired
public void setWeChatDllService(WeChatDllService weChatDllService) {
this.weChatDllService = weChatDllService;
}
@Resource
private WeChatFerryProperties weChatFerryProperties;
@Override
public void receiveMsg(String jsonString) {
// 转发接口处理
receiveMsgForward(jsonString);
// 转为JSON对象
WxMsgDTO dto = JSON.parseObject(jsonString, WxMsgDTO.class);
// TODO 这里可以拓展自己需要的功能
WxPpMsgDTO dto = JSON.parseObject(jsonString, WxPpMsgDTO.class);
// 有开启的群聊配置
if (!CollectionUtils.isEmpty(weChatFerryProperties.getOpenMsgGroups())) {
// 指定处理的群聊
if (weChatFerryProperties.getOpenMsgGroups().contains(dto.getRoomId())) {
// TODO 这里可以拓展自己需要的功能
}
}
log.debug("[收到消息]-[消息内容]-打印:{}", dto);
}
private void receiveMsgForward(String jsonString) {
// 开启转发且转发地址不为空
if (weChatFerryProperties.getReceiveMsgFwdSwitch() && !CollectionUtils.isEmpty(weChatFerryProperties.getReceiveMsgFwdUrls())) {
for (String receiveMsgFwdUrl : weChatFerryProperties.getReceiveMsgFwdUrls()) {
if (!receiveMsgFwdUrl.startsWith("http")) {
continue;
}
try {
String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString);
if (ObjectUtils.isEmpty(responseStr) || !JSONObject.parseObject(responseStr).getString("code").equals("200")) {
log.error("消息转发外部接口,获取响应状态失败!-URL{}", receiveMsgFwdUrl);
}
log.debug("[接收消息]-[转发接收到的消息]-转发消息至:{}", receiveMsgFwdUrl);
} catch (Exception e) {
log.error("消息转发接口[{}]异常:", receiveMsgFwdUrl, e);
}
}
}
}
private Boolean judgeSuccess(String responseStr) {
// 默认为通过
boolean passFlag = false;
if (!ObjectUtils.isEmpty(responseStr)) {
JSONObject jSONObject = JSONObject.parseObject(responseStr);
if (!ObjectUtils.isEmpty(jSONObject)) {
if (!ObjectUtils.isEmpty(jSONObject.get("code"))) {
if (jSONObject.get("code").equals("200")) {
return true;
}
}
}
}
return passFlag;
}
}

View File

@ -4,6 +4,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.dom4j.Attribute;
@ -16,27 +17,27 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
@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());
}
private static final String DEFAULT_ATTRIBUTE_PREFIX = "_";
private static final String DEFAULT_VALUE_PREFIX = "_text";
public static String readFile(String path) throws Exception {
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();
String str = new String(bb.array(), StandardCharsets.UTF_8);
fc.close();
fis.close();
return str;
}
/**
@ -44,15 +45,15 @@ public class XmlJsonConvertUtil {
*
* @param xmlStr
* @return
* @throws DocumentException
*/
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());
log.error("转换失败:", e);
}
return json;
}
@ -68,7 +69,7 @@ public class XmlJsonConvertUtil {
for (Object o : element.attributes()) {
Attribute attr = (Attribute)o;
if (!isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), attr.getValue());
json.put(DEFAULT_ATTRIBUTE_PREFIX + attr.getName(), attr.getValue());
}
}
List<Element> chdEl = element.elements();
@ -110,12 +111,25 @@ public class XmlJsonConvertUtil {
for (Object o : element.attributes()) {
Attribute attr = (Attribute)o;
if (!isEmpty(attr.getValue())) {
json.put("@" + attr.getName(), attr.getValue());
json.put(DEFAULT_ATTRIBUTE_PREFIX + attr.getName(), attr.getValue());
}
}
// 判断是否包含属性
// if (!CollectionUtils.isEmpty(e.attributes())) {
// for (Object o : e.attributes()) {
// Attribute attr = (Attribute)o;
// if (!isEmpty(attr.getValue())) {
// json.put(DEFAULT_ATTRIBUTE_PREFIX + attr.getName(), attr.getValue());
// }
// if (!e.getText().isEmpty()) {
// json.put(DEFAULT_VALUE_PREFIX, e.getText());
// }
// }
// } else {
if (!e.getText().isEmpty()) {
json.put(e.getName(), e.getText());
}
// }
}
}
}
@ -127,4 +141,15 @@ public class XmlJsonConvertUtil {
return false;
}
public static void main(String[] args) {
String xml = "<msgsource></msgsource>";
log.info("xml格式化前:{}", xml);
xml = xml.replaceAll(">[\\s\\p{Zs}]*<","><");
log.info("xml格式化后:{}", xml);
JSONObject json = xml2Json(xml);
System.out.println("xml2Json:" + json.toJSONString());
}
}

View File

@ -14,6 +14,15 @@ spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# 打成jar包必须添加如下配置才能找到页面
thymeleaf:
mode: HTML
cache: false
prefix: classpath:/templates
# 日志配置
logging:
config: classpath:logback-spring.xml
# 本服务参数
wechat:
@ -22,3 +31,21 @@ wechat:
dll-path: E:\WeChatFerry\clients\java\wechat-ferry-mvn\dll\sdk.dll
# socket端口
socket-port: 10086
# 需要开启消息处理的群
open-msg-groups:
- 53257911728@chatroom
# 接收消息转发开关
receive-msg-fwd-switch: false
# 接收消息转发URL
receive-msg-fwd-urls:
- http://
# 发送消息前转发开关
send-msg-front-fwd-switch: false
# 发送消息前转发URL
send-msg-front-fwd-urls:
- http://
# 发送消息后转发开关
send-msg-back-fwd-switch: false
# 发送消息后转发URL
send-msg-back-fwd-urls:
- http://

View File

@ -1,7 +1,7 @@
syntax = "proto3";
package wcf;
option java_package = "com.wechat.ferry.entity.po";
option java_package = "com.wechat.ferry.entity.proto";
enum Functions {
FUNC_RESERVED = 0x00;
@ -234,3 +234,21 @@ message ForwardMsg
uint64 id = 1 [ jstype = JS_STRING ]; // ID
string receiver = 2; // roomId wxid
}
// RoomData内部转换
message RoomData {
repeated RoomMember members = 1;
int32 field_2 = 2;
int32 field_3 = 3;
int32 field_4 = 4;
int32 room_capacity = 5;
int32 field_6 = 6;
int64 field_7 = 7;
int64 field_8 = 8;
message RoomMember {
string wxid = 1;
string name = 2;
int32 state = 3;
}
}