Merge pull request #249 from PathfinderAx/master

[java]-[wechat-ferry-mvn]-项目基础完善,增加对外接口
This commit is contained in:
Changhua 2024-10-10 12:48:12 +08:00 committed by GitHub
commit 268856fe7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 6456 additions and 1968 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 处理层
@ -123,13 +132,44 @@ wechat:
dll-path: /dll/sdk.dll
```
#### 生成proto文件
### 缩写含义
首先特别通用的缩写这里就不在重复了比如DTO,PO这类
请求入参req
请求出参resp
微信wx
个人微信pp
企业微信cp
微信公众号mp
WeChatFerry框架wcf
.gitkeep占位文件比如我们定义一个文件夹之后为了避免文件夹下无内容导致git未提交上所以会建议此文件阅读时直接忽略此类文件即可
### 生成proto文件
本程序已经集成了生成proto文件的maven插件直接install即可生成proto文件且会在打包程序中去除 src/main/resources/proto
下面的内容,只保留实体类中的文件
默认install会重新根据.proto文件重新生成实体如果不想被替换请删除 src/main/resources/proto 下对应的.proto文件即可
#### 本项目自适配类型
有一些查询数据的自定义返回字段,定义了本项目自身的返回类型,所以 proto 文件不要从其他项目拷贝,不然缺失类型导致接口返回乱码自行负责
如:
```
message RoomData
```
### 提交规范
本模块希望大家使用统一提交格式,便于区分
@ -148,4 +188,18 @@ feat(0): [java]-[wechat-ferry-mvn]-基础类目录划分迁移及代码格式
| fix | 缺陷 |
| ... | 其他等git规范中的均可 |
### 说明
下面主要是针对一些合作开发者的咨询做统一回复,可以直接忽略。
#### 目录层级拆分太细
其实这个服务是底层服务,目录分细确实会增加开发量,但是针对于后面的拓展和维护上来说,个人感觉利大于弊,
毕竟功能开发完之后,这个服务很少在变动,更多的是阅读,如果能让大家阅读、维护方便,开发时多花点时间能接受。
当然,如果觉得目录太多,可以自己删除点,这样可以少写点代码,目前本项目还是建议保必要的层级隔离
#### 文件名太长
出发点是为了名称进行统一规范化,便于阅读更加方便,只要了解了基础的缩写含义,其实跟自己平时命名一样,
只不过加上了固定的前缀和后缀而已,这样单从文件名就能分析出功能模块及其含义

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

@ -2,6 +2,7 @@ package com.wechat.ferry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动类
@ -9,6 +10,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author chandler
* @date 2024-09-21 12:19
*/
@EnableScheduling
@SpringBootApplication
public class WeChatFerryApplication {

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,8 @@
package com.wechat.ferry.config;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ -26,4 +29,34 @@ public class WeChatFerryProperties {
*/
private Integer socketPort;
/**
* 需要开启消息处理的群
*/
private List<String> openMsgGroups;
/**
* 接收消息转发开关
*/
private Boolean receiveMsgFwdSwitch = false;
/**
* 接收消息转发URL
*/
private List<String> receiveMsgFwdUrls;
/**
* 发送消息转发标识 1-关闭 2-全转发 3-发送成功才转发
*/
private String sendMsgFwdFlag = "1";
/**
* 发送消息转发URL
*/
private List<String> sendMsgFwdUrls;
/**
* 调用第三方服务客户端成功状态码
*/
private Map<String, String> thirdPartyOkCodes;
}

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,37 @@
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.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendRichTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendXmlMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpWcfContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfLoginInfoResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendPatOnePatMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendRichTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
import com.wechat.ferry.enums.ResponseCodeEnum;
import com.wechat.ferry.service.WeChatDllService;
@ -23,7 +47,7 @@ import lombok.extern.slf4j.Slf4j;
*/
@Slf4j
@RestController
@RequestMapping("/wechat/cgi/dll")
@RequestMapping("/wechat/cgi/wcf")
@Api(tags = "微信消息处理-接口")
public class WeChatDllController {
@ -34,11 +58,197 @@ 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 = "获取登录微信内部识别号UID", notes = "queryLoginWeChatUid")
@PostMapping(value = "/loginWeChatUid")
public TResponse<Object> queryLoginWeChatUid() {
String weChatUid = weChatDllService.queryLoginWeChatUid();
return TResponse.ok(ResponseCodeEnum.SUCCESS, weChatUid);
}
@ApiOperation(value = "获取登录微信信息", notes = "queryLoginWeChatInfo")
@PostMapping(value = "/loginWeChatInfo")
public TResponse<WxPpWcfLoginInfoResp> queryLoginWeChatInfo() {
WxPpWcfLoginInfoResp resp = weChatDllService.queryLoginWeChatInfo();
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "获取消息类型", notes = "queryMsgTypeList")
@PostMapping(value = "/list/msgType")
public TResponse<List<WxPpWcfMsgTypeResp>> queryMsgTypeList() {
List<WxPpWcfMsgTypeResp> list = weChatDllService.queryMsgTypeList();
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取联系人", notes = "queryContactsList")
@PostMapping(value = "/list/contacts")
public TResponse<List<WxPpWcfContactsResp>> queryContactsList() {
List<WxPpWcfContactsResp> list = weChatDllService.queryContactsList();
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "获取可查询数据库", notes = "queryDatabaseSql")
@PostMapping(value = "/list/dbSql")
public TResponse<List<WxPpWcfDatabaseRowResp>> queryDatabaseSql(@Validated @RequestBody WxPpWcfDatabaseSqlReq request) {
List<WxPpWcfDatabaseRowResp> 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 WxPpWcfDatabaseTableReq 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<WxPpWcfGroupMemberResp>> queryGroupMember(@Validated @RequestBody WxPpWcfGroupMemberReq request) {
List<WxPpWcfGroupMemberResp> list = weChatDllService.queryGroupMember(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "发送消息汇总入口", notes = "sendMsgMaster")
@PostMapping(value = "/send/msgMaster")
public TResponse<WxPpWcfSendTextMsgResp> sendMsgMaster(@Validated @RequestBody String jsonString) {
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "发送文本消息(可 @", notes = "sendTextMsg")
@PostMapping(value = "/send/textMsg")
public TResponse<WxPpWcfSendTextMsgResp> sendTextMsg(@Validated @RequestBody WxPpWcfSendTextMsgReq request) {
WxPpWcfSendTextMsgResp resp = weChatDllService.sendTextMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送图片消息", notes = "sendImageMsg")
@PostMapping(value = "/send/imageMsg")
public TResponse<WxPpWcfSendImageMsgResp> sendImageMsg(@Validated @RequestBody WxPpWcfSendImageMsgReq request) {
WxPpWcfSendImageMsgResp resp = weChatDllService.sendImageMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送文件消息", notes = "sendFileMsg")
@PostMapping(value = "/send/fileMsg")
public TResponse<WxPpWcfSendFileMsgResp> sendFileMsg(@Validated @RequestBody WxPpWcfSendFileMsgReq request) {
WxPpWcfSendFileMsgResp resp = weChatDllService.sendFileMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送XML消息", notes = "sendXmlMsg")
@PostMapping(value = "/send/xmlMsg")
public TResponse<WxPpWcfSendXmlMsgResp> sendXmlMsg(@Validated @RequestBody WxPpWcfSendXmlMsgReq request) {
WxPpWcfSendXmlMsgResp resp = weChatDllService.sendXmlMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送表情消息", notes = "sendEmojiMsg")
@PostMapping(value = "/send/emojiMsg")
public TResponse<WxPpWcfSendEmojiMsgResp> sendEmojiMsg(@Validated @RequestBody WxPpWcfSendEmojiMsgReq request) {
WxPpWcfSendEmojiMsgResp resp = weChatDllService.sendEmojiMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "发送富文本消息", notes = "sendRichTextMsg")
@PostMapping(value = "/send/richTextMsg")
public TResponse<WxPpWcfSendRichTextMsgResp> sendRichTextMsg(@Validated @RequestBody WxPpWcfSendRichTextMsgReq request) {
WxPpWcfSendRichTextMsgResp resp = weChatDllService.sendRichTextMsg(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, resp);
}
@ApiOperation(value = "拍一拍群友", notes = "patOnePat")
@PostMapping(value = "/patOnePat")
public TResponse<WxPpWcfSendPatOnePatMsgResp> patOnePat(@Validated @RequestBody WxPpWcfPatOnePatMsgReq request) {
WxPpWcfSendPatOnePatMsgResp resp = weChatDllService.patOnePat(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<WxPpSendPatOnePatMsgResp> friendApply(@Validated @RequestBody WxPpPatOnePatMsgReq request) {
// // WxPpSendPatOnePatMsgResp resp = weChatDllService.patOnePat(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);
// }
}

View File

@ -1,16 +1,19 @@
package com.wechat.ferry.entity.dto;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonIgnore;
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 {
/**
* 是否自己发送的
@ -54,6 +57,21 @@ public class WxMsgDTO {
@ApiModelProperty(value = "消息内容")
private String content;
/**
* 转为JSON的内容
* 对应DLL中的content原始内容
*/
@ApiModelProperty(value = "转为JSON的内容")
private JSONObject jsonContent;
/**
* 引用内容
* 对应DLL中的content原始内容
*/
@JsonIgnore
@ApiModelProperty(value = "消息内容XML")
private String quoteContent;
/**
* 消息发送者
*/

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;
/**
* 请求入参-查询-个微WCF数据库SQL查询
*
* @author chandler
* @date 2024-10-02 17:10
*/
@Data
@ApiModel(value = "wxPpWcfDatabaseSqlReq", description = "个微WCF数据库SQL查询请求入参")
public class WxPpWcfDatabaseSqlReq {
/**
* 数据库名称
*/
@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;
/**
* 请求入参-查询-个微WCF数据库表查询
*
* @author chandler
* @date 2024-10-02 17:55
*/
@Data
@ApiModel(value = "wxPpWcfDatabaseTableReq", description = "个微WCF数据库表查询请求入参")
public class WxPpWcfDatabaseTableReq {
/**
* 数据库名称
*/
@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;
/**
* 请求入参-个微WCF查询群成员
*
* @author chandler
* @date 2024-10-02 20:55
*/
@Data
@ApiModel(value = "wxPpWcfGroupMemberReq", description = "个微WCF查询群成员请求入参")
public class WxPpWcfGroupMemberReq {
/**
* 群编号
*/
@ApiModelProperty(value = "群编号")
private String groupNo;
}

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;
/**
* 请求入参-个微WCF发送拍一拍消息
*
* @author chandler
* @date 2024-10-06 15:50
*/
@Data
@ApiModel(value = "wxPpWcfPatOnePatMsgReq", description = "个微WCF发送拍一拍消息请求入参")
public class WxPpWcfPatOnePatMsgReq {
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
/**
* 要拍的wxid
*/
@ApiModelProperty(value = "要拍的wxid")
private String patUser;
}

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;
/**
* 请求入参-个微WCF发送表情消息
*
* @author chandler
* @date 2024-10-04 23:14
*/
@Data
@ApiModel(value = "wxPpWcfSendEmojiMsgReq", description = "个微WCF发送表情消息请求入参")
public class WxPpWcfSendEmojiMsgReq {
/**
* 资源路径-本地表情路径
*/
@ApiModelProperty(value = "资源路径-本地表情路径")
private String resourcePath;
/**
* 消息接收人
* 消息接收人私聊为 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;
/**
* 请求入参-个微WCF发送文件消息
*
* @author chandler
* @date 2024-10-04 23:08
*/
@Data
@ApiModel(value = "wxPpWcfSendFileMsgReq", description = "个微WCF发送文件消息请求入参")
public class WxPpWcfSendFileMsgReq {
/**
* 资源路径-本地文件路径
*/
@ApiModelProperty(value = "资源路径-本地文件路径")
private String resourcePath;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

View File

@ -0,0 +1,33 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微WCF发送图片消息
*
* @author chandler
* @date 2024-10-04 15:55
*/
@Data
@ApiModel(value = "wxPpWcfSendImageMsgReq", description = "个微WCF发送图片消息请求入参")
public class WxPpWcfSendImageMsgReq {
/**
* 资源路径-本地图片地址
* `C:/Projs/WeChatRobot/TEQuant.jpeg`
* `https://raw.githubusercontent.com/lich0821/WeChatFerry/master/assets/TEQuant.jpg`
*/
@ApiModelProperty(value = "资源路径-本地图片地址")
private String resourcePath;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

View File

@ -0,0 +1,61 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微WCF发送富文本消息
*
* @author chandler
* @date 2024-10-06 15:40
*/
@Data
@ApiModel(value = "wxPpWcfSendRichTextMsgReq", description = "个微WCF发送富文本消息请求入参")
public class WxPpWcfSendRichTextMsgReq {
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
/**
* 左下显示的名字
*/
@ApiModelProperty(value = "左下显示的名字")
private String name;
/**
* 填公众号id 可以显示对应的头像gh_ 开头的
*/
@ApiModelProperty(value = "资源路径-封面图片路径")
private String account;
/**
* 标题最多两行
*/
@ApiModelProperty(value = "标题,最多两行")
private String title;
/**
* 摘要三行
*/
@ApiModelProperty(value = "摘要,三行")
private String digest;
/**
* 点击后跳转的链接
*/
@ApiModelProperty(value = "点击后跳转的链接")
private String jumpUrl;
/**
* 缩略图的链接
*/
@ApiModelProperty(value = "缩略图的链接")
private String thumbnailUrl;
}

View File

@ -0,0 +1,50 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 请求入参-个微WCF发送文本消息
*
* @author chandler
* @date 2024-10-02 20:33
*/
@Data
@ApiModel(value = "wxPpWcfSendTextMsgReq", description = "个微WCF发送文本消息请求入参")
public class WxPpWcfSendTextMsgReq {
/**
* 消息文本
* 消息内容如果是 @ 消息则需要有跟 @ 的人数量相同的 @
* 换行使用 `\\\\n` 单杠
*/
@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,43 @@
package com.wechat.ferry.entity.vo.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微WCF发送XML消息
*
* @author chandler
* @date 2024-10-04 23:11
*/
@Data
@ApiModel(value = "wxPpWcfSendXmlMsgReq", description = "个微WCF发送XML消息请求入参")
public class WxPpWcfSendXmlMsgReq {
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
/**
* XML报文内容
*/
@ApiModelProperty(value = "XML报文内容")
private String xmlContent;
/**
* 资源路径-封面图片路径
*/
@ApiModelProperty(value = "资源路径-封面图片路径")
private String resourcePath;
/**
* xml类型0x21 为小程序
*/
@ApiModelProperty(value = "xml类型")
private Integer xmlType;
}

View File

@ -0,0 +1,102 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微WCF联系人
*
* @author chandler
* @date 2024/10/02 17:01
*/
@Data
@ApiModel(value = "wxPpWcfContactsResp", description = "个微WCF联系人查询请求出参")
public class WxPpWcfContactsResp {
/**
* 微信内部识别号UID
* 原始微信账号ID"wxid_"开头初始默认的微信ID=微信号
*/
@ApiModelProperty(value = "微信内部识别号UID")
private String weChatUid;
/**
* 微信号
*/
@ApiModelProperty(value = "微信号")
private String weChatNo;
/**
* 微信昵称
*/
@ApiModelProperty(value = "微信昵称")
private String weChatNickname;
/**
* 联系人备注
*/
@ApiModelProperty(value = "联系人备注")
private String weChatRemark;
/**
* 国家
*/
@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;
/**
* 请求出参-个微WCF数据库字段
*
* @author chandler
* @date 2024/10/02 17:15
*/
@Data
@ApiModel(value = "wxPpWcfDatabaseFieldResp", description = "个微WCF数据库字段查询请求出参")
public class WxPpWcfDatabaseFieldResp {
/**
* 字段类型
*/
@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;
/**
* 请求出参-个微WCF数据库记录
*
* @author chandler
* @date 2024/10/02 17:14
*/
@Data
@ApiModel(value = "wxPpWcfDatabaseRowResp", description = "个微WCF数据库记录查询请求出参")
public class WxPpWcfDatabaseRowResp {
/**
* 字段列表
*/
@ApiModelProperty(value = "字段列表")
private List<WxPpWcfDatabaseFieldResp> fieldList;
}

View File

@ -0,0 +1,36 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微WCF查询群成员
*
* @author chandler
* @date 2024/10/01 21:26
*/
@Data
@ApiModel(value = "wxPpWcfGroupMemberResp", description = "个微WCF群成员查询请求出参")
public class WxPpWcfGroupMemberResp {
/**
* 微信内部识别号UID
* 原始微信账号ID"wxid_"开头初始默认的微信ID=微信号
*/
@ApiModelProperty(value = "微信内部识别号UID")
private String weChatUid;
/**
* 群内昵称
*/
@ApiModelProperty(value = "群内昵称")
private String groupNickName;
/**
* 状态
*/
@ApiModelProperty(value = "状态")
private String state;
}

View File

@ -0,0 +1,42 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-登录WCF个微信息
*
* @author chandler
* @date 2024/10/05 22:53
*/
@Data
@ApiModel(value = "wxPpWcfLoginInfoResp", description = "登录WCF个微信息查询请求出参")
public class WxPpWcfLoginInfoResp {
/**
* 微信内部识别号UID
* 原始微信账号ID"wxid_"开头初始默认的微信ID=微信号
*/
@ApiModelProperty(value = "微信内部识别号UID")
private String weChatUid;
/**
* 微信昵称
*/
@ApiModelProperty(value = "微信昵称")
private String weChatNickname;
/**
* 手机号
*/
@ApiModelProperty(value = "手机号")
private String phone;
/**
* 文件/图片等父路径
*/
@ApiModelProperty(value = "文件/图片等父路径")
private String homePath;
}

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;
/**
* 请求出参-个微WCF消息类型
*
* @author chandler
* @date 2024/10/01 21:26
*/
@Data
@ApiModel(value = "wxPpWcfMsgTypeResp", description = "个微WCF消息类型查询请求出参")
public class WxPpWcfMsgTypeResp {
/**
* 类型编号
*/
@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;
/**
* 请求出参-个微WCF发送GIF消息
*
* @author chandler
* @date 2024/10/04 23:13
*/
@Data
@ApiModel(value = "wxPpWcfSendEmojiMsgResp", description = "个微WCF发送GIF消息请求出参")
public class WxPpWcfSendEmojiMsgResp {
/**
* 类型编号
*/
@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;
/**
* 请求出参-个微WCF发送文件消息
*
* @author chandler
* @date 2024/10/04 23:07
*/
@Data
@ApiModel(value = "wxPpWcfSendFileMsgResp", description = "个微WCF发送文件消息请求出参")
public class WxPpWcfSendFileMsgResp {
/**
* 类型编号
*/
@ApiModelProperty(value = "类型编号")
private Integer id;
/**
* 类型名称
*/
@ApiModelProperty(value = "类型名称")
private String name;
}

View File

@ -0,0 +1,43 @@
package com.wechat.ferry.entity.vo.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求出参-个微WCF发送图像消息
*
* @author chandler
* @date 2024/10/03 10:17
*/
@Data
@ApiModel(value = "wxPpWcfSendImageMsgResp", description = "个微WCF发送图像消息请求出参")
public class WxPpWcfSendImageMsgResp {
/**
* 状态码
*/
@ApiModelProperty(value = "状态码")
private String code;
/**
* 返回信息
*/
@ApiModelProperty(value = "返回信息")
private String msg;
/**
* 图片地址
*/
@ApiModelProperty(value = "图片地址")
private String path;
/**
* 消息接收人
* 消息接收人私聊为 wxidwxid_xxxxxxxxxxxxxx
* 群聊为 roomidxxxxxxxxxx@chatroom
*/
@ApiModelProperty(value = "消息接收人")
private String recipient;
}

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;
/**
* 请求出参-个微WCF发送拍一拍消息
*
* @author chandler
* @date 2024/10/06 15:52
*/
@Data
@ApiModel(value = "wxPpWcfSendPatOnePatMsgResp", description = "个微WCF发送拍一拍消息请求出参")
public class WxPpWcfSendPatOnePatMsgResp {
/**
* 类型编号
*/
@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;
/**
* 请求出参-个微WCF发送富文本消息
*
* @author chandler
* @date 2024/10/06 15:46
*/
@Data
@ApiModel(value = "wxPpWcfSendRichTextMsgResp", description = "个微WCF发送富文本消息请求出参")
public class WxPpWcfSendRichTextMsgResp {
/**
* 类型编号
*/
@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;
/**
* 请求出参-个微WCF发送文本消息
*
* @author chandler
* @date 2024/10/03 10:17
*/
@Data
@ApiModel(value = "wxPpWcfSendTextMsgResp", description = "个微WCF发送文本消息请求出参")
public class WxPpWcfSendTextMsgResp {
/**
* 类型编号
*/
@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;
/**
* 请求出参-个微WCF发送XML消息
*
* @author chandler
* @date 2024/10/04 23:11
*/
@Data
@ApiModel(value = "wxPpWcfSendXmlMsgResp", description = "个微WCF发送XML消息请求出参")
public class WxPpWcfSendXmlMsgResp {
/**
* 类型编号
*/
@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

@ -4,29 +4,36 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-消息类型
* 枚举-是否
*
* @author chandler
* @date 2024/10/01 15:55
* @date 2023/3/14 10:21
*/
@Getter
@AllArgsConstructor
public enum WeChatMsgTypeEnum {
public enum WhetherEnum {
/**
* 0-未知
* 1-Y-
*/
UNKNOWN("0", "未知"),
YES("1", "", "Y", true),
/**
* 2-N-
*/
NO("2", "", "N", false),
/**
* 未匹配上
*/
UN_MATCH("", null),
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,70 @@
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_MIXED_NO("4", "官方杂号", null),
/**
* 5-公众号
*/
OFFICIAL_ACCOUNT("5", "公众号", "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

@ -0,0 +1,53 @@
package com.wechat.ferry.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 枚举-本服务定义消息类型
*
* @author chandler
* @date 2024/10/01 15:55
*/
@Getter
@AllArgsConstructor
public enum WxPpMsgTypeEnum {
/**
* 0-未知
*/
UNKNOWN("0", "未知", "文本"),
/**
* 1-文本
*/
TEXT("1", "文本", "文本"),
/**
* 2-表情符号
*/
EMOJI("2", "表情符号", "1"),
/**
* 3-表情包(表情图片)
*/
EMOJI_IMAGE("3", "表情包(表情图片)", "47"),
/**
* 4-引用消息
*/
QUOTE("4", "引用消息", "49"),
/**
* 未匹配上
*/
UN_MATCH("", null, "文本"),
// 结束
;
private final String code;
private final String name;
private final String dllCode;
}

View File

@ -8,25 +8,28 @@ import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.util.ObjectUtils;
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;
import com.wechat.ferry.utils.XmlJsonConvertUtil;
import io.sisu.nng.Socket;
import io.sisu.nng.pair.Pair1Socket;
@ -118,7 +121,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 +531,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 +564,48 @@ 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.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);
// 根据消息类型判断 引用-49
if (!ObjectUtils.isEmpty(msg.getContent()) && "49".equals("" + msg.getType())) {
try {
dto.setQuoteContent(content);
JSONObject json = XmlJsonConvertUtil.xml2Json(content);
// 获取第一层级的JSONObject
JSONObject level1 = json.getJSONObject("msg");
if (!ObjectUtils.isEmpty(level1)) {
// 获取第二层级的JSONObject
JSONObject level2 = level1.getJSONObject("appmsg");
if (!ObjectUtils.isEmpty(level2)) {
// 获取field字段的值
String fieldValue = level2.getString("title");
dto.setContent(fieldValue);
}
}
dto.setJsonContent(json);
} catch (Exception e) {
log.error("XML提取报错", e);
// 报错就使用原值
dto.setContent(content);
}
} else {
dto.setContent(content);
}
String jsonString = JSONObject.toJSONString(dto);
try {

View File

@ -1,5 +1,30 @@
package com.wechat.ferry.service;
import java.util.List;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendRichTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendXmlMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpWcfContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfLoginInfoResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendPatOnePatMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendRichTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
/**
* 业务接口-对接原本DLL的接口
*
@ -8,4 +33,176 @@ package com.wechat.ferry.service;
*/
public interface WeChatDllService {
/**
* 当前微信客户端是否登录微信号
*
* @return true-已登录 false-未登录
*
* @author chandler
* @date 2024-10-01 21:20
*/
Boolean loginStatus();
/**
* 获取登录微信内部识别号UID
*
* @return 微信内部识别号UID
*
* @author chandler
* @date 2024-10-01 21:22
*/
String queryLoginWeChatUid();
/**
* 获取登录微信信息
*
* @return 当前登录微信信息
*
* @author chandler
* @date 2024-10-05 22:54
*/
WxPpWcfLoginInfoResp queryLoginWeChatInfo();
/**
* 获取所有消息类型
*
* @return 消息类型列表
*
* @author chandler
* @date 2024-10-01 21:22
*/
List<WxPpWcfMsgTypeResp> queryMsgTypeList();
/**
* 获取所有联系人
*
* @return 联系人列表
*
* @author chandler
* @date 2024-10-02 16:59
*/
List<WxPpWcfContactsResp> queryContactsList();
/**
* 获取可查询数据库
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 17:52
*/
List<WxPpWcfDatabaseRowResp> queryDatabaseSql(WxPpWcfDatabaseSqlReq 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(WxPpWcfDatabaseTableReq request);
/**
* 查询群成员
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 20:59
*/
List<WxPpWcfGroupMemberResp> queryGroupMember(WxPpWcfGroupMemberReq request);
/**
* 发送文本消息 @
*
* @param request 请求入参
* @return 消息发送返回
*
* @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ", "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2");
*
* @author chandler
* @date 2024-10-02 20:40
*/
WxPpWcfSendTextMsgResp sendTextMsg(WxPpWcfSendTextMsgReq request);
/**
* 发送图片消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:06
*/
WxPpWcfSendImageMsgResp sendImageMsg(WxPpWcfSendImageMsgReq request);
/**
* 发送文件消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:15
*/
WxPpWcfSendFileMsgResp sendFileMsg(WxPpWcfSendFileMsgReq request);
/**
* 发送XML消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:15
*/
WxPpWcfSendXmlMsgResp sendXmlMsg(WxPpWcfSendXmlMsgReq request);
/**
* 发送表情消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-04 23:29
*/
WxPpWcfSendEmojiMsgResp sendEmojiMsg(WxPpWcfSendEmojiMsgReq request);
/**
* 发送富文本消息
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-06 15:48
*/
WxPpWcfSendRichTextMsgResp sendRichTextMsg(WxPpWcfSendRichTextMsgReq request);
/**
* 拍一拍
*
* @param request 请求入参
* @return 消息发送返回
*
* @author chandler
* @date 2024-10-06 15:54
*/
WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request);
}

View File

@ -1,8 +1,55 @@
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 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.alibaba.fastjson2.JSONObject;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.entity.proto.Wcf;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendRichTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendTextMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendXmlMsgReq;
import com.wechat.ferry.entity.vo.response.WxPpWcfContactsResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfDatabaseFieldResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfDatabaseRowResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfGroupMemberResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfLoginInfoResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfMsgTypeResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendEmojiMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendFileMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendImageMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendPatOnePatMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendRichTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
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 com.wechat.ferry.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
@ -16,4 +63,430 @@ import lombok.extern.slf4j.Slf4j;
@Service
public class WeChatDllServiceImpl implements WeChatDllService {
private WeChatSocketClient wechatSocketClient;
@Autowired
public void setWechatSocketClient(WeChatSocketClient wechatSocketClient) {
this.wechatSocketClient = wechatSocketClient;
}
@Resource
private WeChatFerryProperties weChatFerryProperties;
@Override
public Boolean loginStatus() {
Boolean status = wechatSocketClient.isLogin();
log.info("[查询]-[登录状态]-status:{}", status);
return status;
}
@Override
public String queryLoginWeChatUid() {
String weChatUid = wechatSocketClient.getSelfWxId();
log.info("[查询]-[登录微信UID]-weChatUid:{}", weChatUid);
return weChatUid;
}
@Override
public WxPpWcfLoginInfoResp queryLoginWeChatInfo() {
WxPpWcfLoginInfoResp resp = new WxPpWcfLoginInfoResp();
Wcf.UserInfo userInfo = wechatSocketClient.getUserInfo();
if (!ObjectUtils.isEmpty(userInfo)) {
resp.setWeChatUid(userInfo.getWxid());
resp.setWeChatNickname(userInfo.getName());
resp.setPhone(userInfo.getMobile());
resp.setHomePath(userInfo.getHome());
}
log.info("[查询]-[获取登录微信信息]-resp:{}", resp);
return resp;
}
@Override
public List<WxPpWcfMsgTypeResp> queryMsgTypeList() {
List<WxPpWcfMsgTypeResp> list = new ArrayList<>();
Map<Integer, String> msgTypeMap = wechatSocketClient.getMsgTypes();
if (!CollectionUtils.isEmpty(msgTypeMap)) {
WxPpWcfMsgTypeResp resp;
for (Map.Entry<Integer, String> entry : msgTypeMap.entrySet()) {
resp = new WxPpWcfMsgTypeResp();
resp.setId(entry.getKey());
resp.setName(entry.getValue());
list.add(resp);
}
}
log.info("[查询]-[所有消息类型]-共查到:{}条", list.size());
return list;
}
@Override
public List<WxPpWcfContactsResp> queryContactsList() {
List<WxPpWcfContactsResp> list = new ArrayList<>();
List<Wcf.RpcContact> rpcContactList = wechatSocketClient.getContacts();
if (!CollectionUtils.isEmpty(rpcContactList)) {
for (Wcf.RpcContact rpcContact : rpcContactList) {
WxPpWcfContactsResp vo = new WxPpWcfContactsResp();
vo.setWeChatUid(rpcContact.getWxid());
vo.setWeChatNo(rpcContact.getCode());
vo.setWeChatRemark(rpcContact.getRemark());
vo.setWeChatNickname(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())) {
List<String> mixedNoList = new ArrayList<>();
// 朋友推荐消息
mixedNoList.add("fmessage");
// 语音记事本
mixedNoList.add("medianote");
// 漂流瓶
mixedNoList.add("floatbottle");
// 文件传输助手
mixedNoList.add("filehelper");
// 新闻
mixedNoList.add("newsapp");
// 微信团队
mixedNoList.add("weixin");
// 微信支付 wxzhifu
// mixedNoList.add("gh_3dfda90e39d6");
// 微信公开课 wx-gongkaike
// mixedNoList.add("gh_c46cbbfa1de9");
// 微信运动 WeRun-WeChat
// mixedNoList.add("gh_43f2581f6fd6");
// 微信游戏 game
// mixedNoList.add("gh_25d9ac85a4bc");
// 微信开发者
// mixedNoList.add("gh_56fc3b00cc4f");
// 微信搜一搜 wechat_search
// mixedNoList.add("gh_f08f54ae25a4");
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()) || rpcContact.getWxid().endsWith("@im.chatroom")) {
// 群聊 @im.chatroom 这种是很早之前的格式单独例举
vo.setType(WxContactsTypeEnum.GROUP.getCode());
vo.setTypeLabel(WxContactsTypeEnum.GROUP.getName());
} else if (mixedNoList.contains(rpcContact.getWxid())) {
// 官方杂号
vo.setType(WxContactsTypeEnum.OFFICIAL_MIXED_NO.getCode());
vo.setTypeLabel(WxContactsTypeEnum.OFFICIAL_MIXED_NO.getName());
} else if (rpcContact.getWxid().startsWith(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getAffix())) {
// 微信公众号
vo.setType(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getCode());
vo.setTypeLabel(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getName());
} else if ("wxid_2876568766325".equals(rpcContact.getWxid()) || "wxid_2965349653612".equals(rpcContact.getWxid())
|| "wxid_4302923029011".equals(rpcContact.getWxid()) || "mphelper".equals(rpcContact.getWxid())
|| "weixinguanhaozhushou".equals(rpcContact.getWxid())) {
// 应用宝 yingyongbao wxid_2876568766325
// i黑马 iheima wxid_2965349653612
// 丁香医生 DingXiangYiSheng wxid_4302923029011
// 公众平台安全助手 mphelper
// 微信公众平台 weixingongzhong weixinguanhaozhushou
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<WxPpWcfDatabaseRowResp> queryDatabaseSql(WxPpWcfDatabaseSqlReq request) {
List<WxPpWcfDatabaseRowResp> 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) {
WxPpWcfDatabaseRowResp rowVo = new WxPpWcfDatabaseRowResp();
List<WxPpWcfDatabaseFieldResp> fieldVoList = new ArrayList<>();
for (Wcf.DbField dbField : dbRow.getFieldsList()) {
WxPpWcfDatabaseFieldResp fieldVo = new WxPpWcfDatabaseFieldResp();
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(WxPpWcfDatabaseTableReq request) {
log.info("[查询]-[查询表]-request:{}", request);
Map<String, String> wcfMap = wechatSocketClient.getDbTables(request.getDatabaseName());
log.info("[查询]-[查询表]-查到:{}", wcfMap);
return Collections.emptyList();
}
@Override
public List<WxPpWcfGroupMemberResp> queryGroupMember(WxPpWcfGroupMemberReq request) {
List<WxPpWcfGroupMemberResp> 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)) {
WxPpWcfGroupMemberResp vo = new WxPpWcfGroupMemberResp();
for (Wcf.DbField dbField : dbFieldList) {
if ("UserName".equals(dbField.getColumn())) {
vo = new WxPpWcfGroupMemberResp();
String content = (String)converterSqlVal(dbField.getType(), dbField.getContent());
vo.setWeChatUid(content);
}
if ("NickName".equals(dbField.getColumn())) {
String content = (String)converterSqlVal(dbField.getType(), dbField.getContent());
vo.setGroupNickName(content);
dbMap.put(vo.getWeChatUid(), vo.getGroupNickName());
}
}
}
}
}
// 查询群名称
if (!CollectionUtils.isEmpty(wcfList)) {
for (Wcf.DbRow dbRow : wcfList) {
List<Wcf.DbField> dbFieldList = dbRow.getFieldsList();
if (!ObjectUtils.isEmpty(dbFieldList)) {
WxPpWcfGroupMemberResp vo = new WxPpWcfGroupMemberResp();
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 WxPpWcfGroupMemberResp();
vo.setWeChatUid(member.getWxid());
String nickName = member.getName();
if (ObjectUtils.isEmpty(nickName)) {
// 如果没有设置群昵称则默认为微信名称
nickName = dbMap.get(member.getWxid());
}
vo.setGroupNickName(nickName);
vo.setState(String.valueOf(member.getState()));
list.add(vo);
}
} catch (InvalidProtocolBufferException e) {
log.error("异常:", e);
}
}
}
}
}
}
log.info("[查询]-[查询群成员]-共查到:{}条", list.size());
return list;
}
@Override
public WxPpWcfSendTextMsgResp sendTextMsg(WxPpWcfSendTextMsgReq request) {
log.info("[发送消息]-[文本消息]-入参打印:{}", request);
String atUser = "";
if (request.getIsAtAll()) {
// 艾特全体仅管理员有效
atUser = "@all";
} else {
// 处理艾特的人员
if (!CollectionUtils.isEmpty(request.getAtUsers())) {
atUser = String.join(",", request.getAtUsers());
}
}
// 0 为成功其他失败
int state = wechatSocketClient.sendText(request.getMsgText(), request.getRecipient(), atUser);
log.info("[发送消息]-[文本消息]-处理结束");
// 转发处理
String stringJson = JSON.toJSONString(request);
if ("2".equals(weChatFerryProperties.getSendMsgFwdFlag())) {
// 不管消息是否发送成功均转发
sendMsgForward(stringJson);
} else if ("3".equals(weChatFerryProperties.getSendMsgFwdFlag()) && 0 == state) {
// 发送成功才转发
sendMsgForward(stringJson);
}
return null;
}
@Override
public WxPpWcfSendImageMsgResp sendImageMsg(WxPpWcfSendImageMsgReq request) {
WxPpWcfSendImageMsgResp resp = new WxPpWcfSendImageMsgResp();
int state = wechatSocketClient.sendImage(request.getResourcePath(), request.getRecipient());
return null;
}
@Override
public WxPpWcfSendFileMsgResp sendFileMsg(WxPpWcfSendFileMsgReq request) {
int state = wechatSocketClient.sendFile(request.getResourcePath(), request.getRecipient());
return null;
}
@Override
public WxPpWcfSendXmlMsgResp sendXmlMsg(WxPpWcfSendXmlMsgReq request) {
int state = wechatSocketClient.sendXml(request.getRecipient(), request.getXmlContent(), request.getResourcePath(), request.getXmlType());
return null;
}
@Override
public WxPpWcfSendEmojiMsgResp sendEmojiMsg(WxPpWcfSendEmojiMsgReq request) {
int state = wechatSocketClient.sendEmotion(request.getResourcePath(), request.getRecipient());
return null;
}
@Override
public WxPpWcfSendRichTextMsgResp sendRichTextMsg(WxPpWcfSendRichTextMsgReq request) {
Wcf.RichText richTextMsg = Wcf.RichText.newBuilder().setName(request.getName()).setAccount(request.getAccount()).setTitle(request.getTitle())
.setDigest(request.getDigest()).setUrl(request.getJumpUrl()).setThumburl(request.getThumbnailUrl()).setReceiver(request.getRecipient())
.build();
Wcf.Request wcfReq = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_RICH_TXT_VALUE).setRt(richTextMsg).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(wcfReq);
return null;
}
@Override
public WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request) {
Wcf.PatMsg patMsg = Wcf.PatMsg.newBuilder().setRoomid(request.getRecipient()).setWxid(request.getPatUser()).build();
Wcf.Request wcfReq = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_PAT_MSG_VALUE).setPm(patMsg).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(wcfReq);
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;
}
private void sendMsgForward(String jsonString) {
// 开启转发且转发地址不为空
if (!CollectionUtils.isEmpty(weChatFerryProperties.getSendMsgFwdUrls())) {
for (String receiveMsgFwdUrl : weChatFerryProperties.getSendMsgFwdUrls()) {
if (!receiveMsgFwdUrl.startsWith("http")) {
continue;
}
try {
String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString);
if (judgeSuccess(responseStr)) {
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) && !CollectionUtils.isEmpty(weChatFerryProperties.getThirdPartyOkCodes())) {
Map<String, String> codeMap = weChatFerryProperties.getThirdPartyOkCodes();
for (Map.Entry<String, String> entry : codeMap.entrySet()) {
if (!ObjectUtils.isEmpty(jSONObject.get(entry.getKey())) && jSONObject.get(entry.getKey()).equals(entry.getValue())) {
passFlag = true;
break;
}
}
}
}
return passFlag;
}
}

View File

@ -1,10 +1,19 @@
package com.wechat.ferry.service.impl;
import java.util.Map;
import javax.annotation.Resource;
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.WeChatMsgService;
import com.wechat.ferry.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
@ -18,12 +27,61 @@ import lombok.extern.slf4j.Slf4j;
@Service
public class WeChatMsgServiceImpl implements WeChatMsgService {
@Resource
private WeChatFerryProperties weChatFerryProperties;
@Override
public void receiveMsg(String jsonString) {
// 转发接口处理
receiveMsgForward(jsonString);
// 转为JSON对象
WxMsgDTO dto = JSON.parseObject(jsonString, WxMsgDTO.class);
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 (judgeSuccess(responseStr)) {
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) && !CollectionUtils.isEmpty(weChatFerryProperties.getThirdPartyOkCodes())) {
Map<String, String> codeMap = weChatFerryProperties.getThirdPartyOkCodes();
for (Map.Entry<String, String> entry : codeMap.entrySet()) {
if (!ObjectUtils.isEmpty(jSONObject.get(entry.getKey())) && jSONObject.get(entry.getKey()).equals(entry.getValue())) {
passFlag = true;
break;
}
}
}
}
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;
@ -11,6 +12,7 @@ import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.ObjectUtils;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
@ -20,9 +22,10 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class XmlJsonConvertUtil {
public static String readFile(String path) {
String str = "";
try {
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();
@ -30,13 +33,11 @@ public class XmlJsonConvertUtil {
// fc向buffer中读入数据
fc.read(bb);
bb.flip();
str = new String(bb.array(), "UTF8");
String str = new String(bb.array(), StandardCharsets.UTF_8);
fc.close();
fis.close();
} catch (Exception e) {
log.error("异常:{} ", e.getMessage());
}
return str;
}
/**
@ -44,15 +45,23 @@ 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);
// 存放根节点
if (!ObjectUtils.isEmpty(json)) {
Element rootElement = doc.getRootElement();
JSONObject wrapperJson = new JSONObject();
wrapperJson.put(rootElement.getName(), json);
json = wrapperJson;
}
} catch (DocumentException e) {
log.error("异常:{} ", e.getMessage());
log.error("转换失败:", e);
}
return json;
}
@ -68,7 +77,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 +119,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 +149,15 @@ public class XmlJsonConvertUtil {
return false;
}
public static void main(String[] args) {
String xml = "<msg><appmsg appid=\"\" sdkver=\"0\"><title>就是个网页哇</title><des /><username /></appmsg></msg>";
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://localhost:9001/msg
# 发送消息转发标识 1-关闭 2-全转发 3-发送成功才转发
send-msg-fwd-flag: '1'
# 发送消息转发URL
send-msg-fwd-urls:
- http://localhost:9001/msg
# 调用第三方服务客户端成功状态码
third-party-ok-codes:
# key:状态码字段 val:状态码值
code: '200'

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;
}
}