Merge pull request #303 from PathfinderAx/develop

feat(0): [java]-[mvn]-功能更新及缺陷修复 Close #300
This commit is contained in:
Changhua 2025-01-04 20:42:01 +08:00 committed by GitHub
commit e218167307
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 1829 additions and 400 deletions

View File

@ -1,31 +1,81 @@
## v39.3.3 ## v39.3.3
### 版本列表
下载地址:[v39.3.3](https://github.com/lich0821/WeChatFerry/releases/tag/v39.3.3)
| 名称 | 版本 | 文件名 |
|-----------------|-----------|---------------------------|
| 微信客户端 | 3.9.11.25 | WeChatSetup-3.9.11.25.exe |
| WeChatFerry-SDK | 39.3.3 | v39.3.3.zip |
### 功能列表 ### 功能列表
| 接口名 | 地址 | 是否支持 | 备注 | | 接口名 | 地址 | 是否支持 | 备注 |
|----------------|-------------------|------|-------------------| |----------------|------------------------|------|-------------------|
| 查询登录状态 | /loginStatus | √ | 已测试 | | 查询登录状态 | /loginStatus | ✔️ | 已测试 |
| 获取登录微信内部识别号UID | /loginWeChatUid | √ | 已测试 | | 获取登录微信内部识别号UID | /loginWeChatUid | ✔️ | 已测试 |
| 获取登录微信信息 | /loginWeChatInfo | √ | 已测试 | | 获取登录微信信息 | /loginWeChatInfo | ✔️ | 已测试 |
| 获取消息类型列表 | /list/msgType | √ | 已测试 | | 获取消息类型列表 | /list/msgType | ✔️ | 已测试 |
| 获取联系人列表 | /list/contacts | √ | 已测试 | | 获取联系人列表 | /list/contacts | ✔️ | 已测试 |
| 获取数据库表名称列表 | /list/dbTableName | √ | 已测试 | | 获取数据库表名称列表 | /list/dbTableName | ✔️ | 已测试 |
| 获取指定数据库中的表列表 | /list/dbTable | √ | 已测试 | | 获取指定数据库中的表列表 | /list/dbTable | ✔️ | 已测试 |
| 执行数据库查询SQL | /exec/dbQuerySql | √ | 已测试 | | 执行数据库查询SQL | /exec/dbQuerySql | ✔️ | 已测试 |
| 查询群成员 | /list/groupMember | √ | 已测试 | | 发送消息汇总入口 | /send/msgMaster | ❌ | 预留 |
| 发送消息汇总入口 | /send/msgMaster | x | 预留 | | 发送文本消息 | /send/textMsg | ✔️ | 已测试 |
| 发送文本消息 | /send/textMsg | x | 该版本不支持 | | 发送富文本消息 | /send/richTextMsg | ❌ | 缩略图参数需要为空,否则会发送失败 |
| 发送富文本消息 | /send/richTextMsg | x | 缩略图参数需要为空,否则会发送失败 | | 发送XML消息 | /send/xmlMsg | ❌ | 该版本不支持 |
| 发送XML消息 | /send/xmlMsg | ? | 待测试 | | 发送图片消息 | /send/imageMsg | ✔️ | 已测试 |
| 发送图片消息 | /send/imageMsg | √ | 已测试 | | 发送表情消息 | /send/emojiMsg | ❌ | 该版本不支持 |
| 发送表情消息 | /send/emojiMsg | x | 该版本不支持 | | 发送文件消息 | /send/fileMsg | ❌ | 该版本不支持 |
| 发送文件消息 | /send/fileMsg | x | 该版本不支持 | | 拍一拍群友 | /patOnePat | ✔️ | 已测试 |
| 拍一拍群友 | /patOnePat | √ | 已测试 | | 撤回消息 | /revokeMsg | ❌ | 该版本不支持 |
| 通过好友申请 | /passFriendApply | ❌ | 该版本不支持 |
| 添加群成员为微信好友 | /addFriend/groupMember | ❔ | 待测试 |
| 查询群成员 | /groupMember/list | ✔️ | 已测试 |
| 邀请群成员 | /groupMember/invite | ❔ | 待测试 |
| 删除群成员 | /groupMember/delete | ❔ | 待测试 |
| 查询朋友圈 | /friendCircle | ❔ | 待测试 |
| 接收转账 | /receiveTransfer | ❌ | 该版本不支持 |
### 已知BUG ### 已知BUG
- 1.发送表情微信客户端闪退 - `待修复` - 1.发送表情微信客户端闪退 - `待修复`
- 2.发送富文本包含thumbnailUrl参数会导致消息发送不出去 - `待修复` - 2.发送富文本包含thumbnailUrl参数会导致消息发送不出去 - `待修复`
- 3.发送文件成功之后客户端崩溃 - `待修复`
### 2025-01-04
#### ⛰️ Features
- 退群监测功能关闭,待完善,目前未开启
- 说明文档更新
#### 🐛 Bug fixes
- 微信端退出之后,调用接口返回客户端状态异常提示
### 2024-12-27
#### ⛰️ Features
- 查询群成员返回类新增字段
- 新增退群监测功能
- 说明文档更新
### 2024-12-25
#### ⛰️ Features
- 新增通过好友申请接口
- 新增添加群成员为微信好友接口
- 新增邀请群成员接口
- 新增删除群成员接口
- 新增刷新朋友圈接口
- 新增撤回消息接口
- 接收转账
- 查询群成员请求地址变更
- 消息回调配置文件参数名称修改
- 封装接收到消息之后的业务操作类
### 2024-12-24 ### 2024-12-24
@ -45,3 +95,53 @@
- 适配SDK39.3.3版本 - 适配SDK39.3.3版本
- wcf.proto文件部分字段类型修改 - wcf.proto文件部分字段类型修改
- 消息转发适配多种消息类型 - 消息转发适配多种消息类型
<br/>
___
<br/><br/>
## v39.2.4 - 推荐✨
### 版本列表
下载地址:[v39.2.4](https://github.com/lich0821/WeChatFerry/releases/tag/v39.2.4)
| 名称 | 版本 | 文件名 |
|-----------------|-----------|---------------------------|
| 微信客户端 | 3.9.10.27 | WeChatSetup-3.9.10.27.exe |
| WeChatFerry-SDK | 39.2.4 | v39.2.4.zip |
### 功能列表
| 接口名 | 地址 | 是否支持 | 备注 |
|----------------|------------------------|------|--------|
| 查询登录状态 | /loginStatus | ✔️ | 已测试 |
| 获取登录微信内部识别号UID | /loginWeChatUid | ✔️ | 已测试 |
| 获取登录微信信息 | /loginWeChatInfo | ✔️ | 已测试 |
| 获取消息类型列表 | /list/msgType | ✔️ | 已测试 |
| 获取联系人列表 | /list/contacts | ✔️ | 已测试 |
| 获取数据库表名称列表 | /list/dbTableName | ✔️ | 已测试 |
| 获取指定数据库中的表列表 | /list/dbTable | ✔️ | 已测试 |
| 执行数据库查询SQL | /exec/dbQuerySql | ✔️ | 已测试 |
| 发送消息汇总入口 | /send/msgMaster | ❌ | 预留 |
| 发送文本消息 | /send/textMsg | ✔️ | 已测试 |
| 发送富文本消息 | /send/richTextMsg | ✔️ | 已测试 |
| 发送XML消息 | /send/xmlMsg | ❌ | 该版本不支持 |
| 发送图片消息 | /send/imageMsg | ✔️ | 已测试 |
| 发送表情消息 | /send/emojiMsg | ✔️ | 已测试 |
| 发送文件消息 | /send/fileMsg | ✔️ | 已测试 |
| 拍一拍群友 | /patOnePat | ✔️ | 已测试 |
| 撤回消息 | /revokeMsg | ❌ | 该版本不支持 |
| 通过好友申请 | /passFriendApply | ❌ | 该版本不支持 |
| 添加群成员为微信好友 | /addFriend/groupMember | ❔ | 待测试 |
| 查询群成员 | /groupMember/list | ✔️ | 已测试 |
| 邀请群成员 | /groupMember/invite | ❔ | 待测试 |
| 删除群成员 | /groupMember/delete | ❔ | 待测试 |
| 查询朋友圈 | /friendCircle | ❔ | 待测试 |
| 接收转账 | /receiveTransfer | ❌ | 该版本不支持 |
<br/>
___

View File

@ -13,7 +13,7 @@
|-----------------|-----------|----| |-----------------|-----------|----|
| JDK | 1.8+ | √ | | JDK | 1.8+ | √ |
| Maven | 3.8+ | √ | | Maven | 3.8+ | √ |
| 微信 | 3.9.11.25 | √ | | 微信客户端 | 3.9.11.25 | √ |
| WeChatFerry-SDK | 39.3.3 | √ | | WeChatFerry-SDK | 39.3.3 | √ |
| MySQL | 8.0+ | 备用 | | MySQL | 8.0+ | 备用 |
@ -35,6 +35,15 @@
把刚下载的最新发布文件解压到本项目中的 dll 文件目录下,直接替换原因文件即可 把刚下载的最新发布文件解压到本项目中的 dll 文件目录下,直接替换原因文件即可
替换 `clients/java/wechat-ferry-mvn/dll` 目录下(也可以在配置文件中改为自定义的目录)
- sdk.dll
- spy.dll
- spy_debug.dll
> 如果之前已经使用本项目启动过微信,此时替换发现替换不了,是因为正则运行的微信客户端正在使用该文件,
> 请退出并关闭微信客户端之后再进行替换
### 修改配置文件 ### 修改配置文件
配置文件src/main/resources/application.yml 配置文件src/main/resources/application.yml
@ -72,7 +81,7 @@ swagger地址http://localhost:9201/swagger-ui/index.html
### 核心依赖 ### 核心依赖
| 依赖 | 版本 | 说明 | | 依赖 | 版本 | 说明 |
|---------------|--------|----------| |---------------|-------------|----------|
| Spring Boot | 2.7.18 | 基础框架 | | Spring Boot | 2.7.18 | 基础框架 |
| protobuf-java | 3.22.2 | rpc | | protobuf-java | 3.22.2 | rpc |
| jna | 5.6.0 | 态访问系统本地库 | | jna | 5.6.0 | 态访问系统本地库 |
@ -80,6 +89,8 @@ swagger地址http://localhost:9201/swagger-ui/index.html
| fastjson2 | 2.0.52 | 序列化 | | fastjson2 | 2.0.52 | 序列化 |
| dom4j | 2.1.3 | XML解析包 | | dom4j | 2.1.3 | XML解析包 |
| httpclient | 4.5.13 | 客户端请求 | | httpclient | 4.5.13 | 客户端请求 |
| validation | 2.0.1.Final | 参数校验 |
| springfox | 3.0.0 | swagger3 |
### 模块结构 ### 模块结构
@ -93,16 +104,22 @@ wechat-ferry-mvn
│ ├─main 重启命令 │ ├─main 重启命令
│ │ ├─java(com.wechat.ferry) java代码目录 │ │ ├─java(com.wechat.ferry) java代码目录
│ │ │ ├─config 配置 │ │ │ ├─config 配置
│ │ │ ├─constant 常量
│ │ │ ├─controller 控制层(API接口)
│ │ │ ├─entity 聚合模型 │ │ │ ├─entity 聚合模型
│ │ │ │ ├─dto DTO模型 │ │ │ │ ├─dto DTO模型
│ │ │ │ ├─po 数据库实体(与表结构一一对应,否则请使用DTO) │ │ │ │ ├─po 数据库实体(与表结构一一对应,否则请使用DTO)
│ │ │ │ ├─proto PB实体 │ │ │ │ ├─proto PB实体
│ │ │ │ └─vo 视图层返回体目录 │ │ │ │ └─vo 视图层返回体目录
│ │ │ ├─enums 枚举 │ │ │ ├─enums 枚举
│ │ │ ├─exception 异常封装
│ │ │ ├─handle 处理层 │ │ │ ├─handle 处理层
│ │ │ ├─service 业务接口 │ │ │ ├─service 业务
│ │ │ │ └─impl 业务实现类 │ │ │ │ └─impl 业务实现类
│ │ │ ├─utils 工具类 │ │ │ ├─strategy 策略层
│ │ │ │ └─impl 策略实现类(如接收到消息之后的事件处理可以放在这里)
│ │ │ ├─task 定时任务
│ │ │ ├─utils 工具层
│ │ │ └─WcferryApplication.java 启动类 │ │ │ └─WcferryApplication.java 启动类
│ │ │ │ │ │
│ │ │resources 资源目录 │ │ │resources 资源目录

View File

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

View File

@ -0,0 +1,15 @@
package com.wechat.ferry.aggregation.facade;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* 聚合模型类-联系人
*
* @author chandler
* @date 2023-06-08 22:39:53
*/
@Slf4j
@Data
public class ChatRoomDo {
}

View File

@ -0,0 +1,190 @@
package com.wechat.ferry.aggregation.facade;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.entity.po.wcf.Contact;
import com.wechat.ferry.entity.proto.Wcf;
import com.wechat.ferry.enums.DatabaseNameEnum;
import com.wechat.ferry.enums.WxContactsMixedEnum;
import com.wechat.ferry.enums.WxContactsOfficialEnum;
import com.wechat.ferry.enums.WxContactsTypeEnum;
import com.wechat.ferry.handle.WeChatSocketClient;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* 聚合模型类-联系人
*
* @author chandler
* @date 2023-06-08 22:39:53
*/
@Slf4j
@Data
public class ContactDo extends Contact {
/**
* 微信内部识别号UID
*/
@ApiModelProperty(value = "微信内部识别号UID")
private String weChatUid;
/**
* 联系人类型
*/
@ApiModelProperty(value = "联系人类型")
private String contactType;
/**
* 展示名称
* 有备注优先展示备注
*/
@ApiModelProperty(value = "展示名称")
private String showName;
/**
* 根据自定义SQL查询联系人列表
*
* @param wechatSocketClient 通信客户端
* @param weChatFerryProperties 配置文件
*
* @author chandler
* @date 2024-12-27 16:06
*/
public List<ContactDo> queryContactListBySql(WeChatSocketClient wechatSocketClient, WeChatFerryProperties weChatFerryProperties) {
List<ContactDo> list = new ArrayList<>();
// 查询联系人
List<Wcf.DbRow> dbContactList = wechatSocketClient.querySql(DatabaseNameEnum.MICRO_MSG.getCode(),
"SELECT UserName, Alias, DelFlag, Type, VerifyFlag, Remark, NickName, LabelIDList, DomainList, ChatRoomType, PYInitial, QuanPin, RemarkPYInitial, RemarkQuanPin, ChatRoomNotify FROM Contact;");
if (!CollectionUtils.isEmpty(dbContactList)) {
for (Wcf.DbRow dbRow : dbContactList) {
List<Wcf.DbField> dbFieldList = dbRow.getFieldsList();
if (!ObjectUtils.isEmpty(dbFieldList)) {
ContactDo po = new ContactDo();
for (Wcf.DbField dbField : dbFieldList) {
String content = (String)wechatSocketClient.convertSqlVal(dbField.getType(), dbField.getContent());
// 用户名
if ("UserName".equals(dbField.getColumn())) {
po.setUserName(content);
po.setWeChatUid(content);
// 设置类型
String type = convertContactType(content, weChatFerryProperties);
po.setContactType(type);
}
// 用户名
if ("Alias".equals(dbField.getColumn())) {
po.setAlias(content);
}
// 昵称
if ("NickName".equals(dbField.getColumn())) {
po.setNickname(content);
}
// 删除标志
if ("DelFlag".equals(dbField.getColumn())) {
po.setDelFlag(Integer.valueOf(content));
}
//
if ("VerifyFlag".equals(dbField.getColumn())) {
po.setVerifyFlag(Integer.valueOf(content));
}
//
if ("Remark".equals(dbField.getColumn())) {
po.setRemark(content);
}
//
if ("LabelIDList".equals(dbField.getColumn())) {
po.setLabelIdList(content);
}
//
if ("DomainList".equals(dbField.getColumn())) {
po.setDomainList(content);
}
//
if ("ChatRoomType".equals(dbField.getColumn())) {
po.setChatRoomType(Integer.valueOf(content));
}
}
list.add(po);
}
}
}
return list;
}
/**
* 转换联系人类型
* 类型判断,存在优先级的官方杂号优先级高于微信公众号(如果定义重复了常规禁止重复手机端和电脑端分类不同)
*
* @param weChatUid 微信识别号
* @param weChatFerryProperties 配置文件
*
* @author chandler
* @date 2024-12-27 15:56
*/
public static String convertContactType(String weChatUid, WeChatFerryProperties weChatFerryProperties) {
String type = "";
// 官方杂号集合
Map<String, String> mixedNoMap = WxContactsMixedEnum.toCodeNameMap();
mixedNoMap.putAll(convertContactsTypeProperties(weChatFerryProperties.getContactsTypeMixed()));
// 公众号
Map<String, String> officialMap = WxContactsOfficialEnum.toCodeNameMap();
officialMap.putAll(convertContactsTypeProperties(weChatFerryProperties.getContactsTypeOfficial()));
// 类型判断,存在优先级的官方杂号优先级高于微信公众号(如果定义重复了常规禁止重复手机端和电脑端分类不同)
if (weChatUid.endsWith(WxContactsTypeEnum.WORK.getAffix())) {
// 企微
type = WxContactsTypeEnum.WORK.getCode();
} else if (weChatUid.endsWith(WxContactsTypeEnum.GROUP.getAffix()) || weChatUid.endsWith("@im.chatroom")) {
// 群聊 @im.chatroom 这种是很早之前的格式单独例举
type = WxContactsTypeEnum.GROUP.getCode();
} else if (mixedNoMap.containsKey(weChatUid)) {
// 官方杂号
type = WxContactsTypeEnum.OFFICIAL_MIXED_NO.getCode();
} else if (weChatUid.startsWith(WxContactsTypeEnum.OFFICIAL_ACCOUNT.getAffix())) {
// 微信公众号
type = WxContactsTypeEnum.OFFICIAL_ACCOUNT.getCode();
} else if (officialMap.containsKey(weChatUid)) {
type = WxContactsTypeEnum.OFFICIAL_ACCOUNT.getCode();
} else {
// 个微
type = WxContactsTypeEnum.PERSON.getCode();
}
return type;
}
/**
* 转换联系人类型配置
*
* @param list 配置参数
* @return map key:code val:name
*
* @author chandler
* @date 2024-12-24 16:55
*/
public static Map<String, String> convertContactsTypeProperties(List<String> list) {
Map<String, String> map = new HashMap<>();
if (!CollectionUtils.isEmpty(list)) {
for (String str : list) {
String key = str;
String val = str;
// 存在名称则分割
if (str.contains("|")) {
int index = str.indexOf("|");
key = str.substring(0, index);
val = str.substring(index + 1);
}
map.put(key, val);
}
}
return map;
}
}

View File

@ -81,7 +81,7 @@ public class WeChatConfiguration {
// 只打印 // 只打印
// wechatSocketClient.printWxMsg(wechatSocketClient.getMsg()); // wechatSocketClient.printWxMsg(wechatSocketClient.getMsg());
// 转发到boot项目进行消息处理 // 转发到boot项目进行消息处理
wechatSocketClient.forwardMsg(wechatSocketClient.getMsg(), url); wechatSocketClient.localCallbackAnalyzeMsg(wechatSocketClient.getMsg(), url);
} }
} }
}); });

View File

@ -47,24 +47,24 @@ public class WeChatFerryProperties {
private List<String> openMsgGroups; private List<String> openMsgGroups;
/** /**
* 接收消息转发开关 * 接收消息回调开关
*/ */
private Boolean receiveMsgFwdSwitch = false; private Boolean receiveMsgCallbackSwitch = false;
/** /**
* 接收消息转发URL * 接收消息回调地址
*/ */
private List<String> receiveMsgFwdUrls; private List<String> receiveMsgCallbackUrls;
/** /**
* 发送消息转发标识 1-关闭 2-全转发 3-发送成功才转发 * 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调
*/ */
private String sendMsgFwdFlag = "1"; private String sendMsgCallbackFlag = "1";
/** /**
* 发送消息转发URL * 发送消息回调地址
*/ */
private List<String> sendMsgFwdUrls; private List<String> sendMsgCallbackUrls;
/** /**
* 调用第三方服务客户端成功状态码 * 调用第三方服务客户端成功状态码

View File

@ -10,10 +10,16 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.wechat.ferry.entity.TResponse; import com.wechat.ferry.entity.TResponse;
import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfReceiveTransferReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfRevokeMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
@ -115,13 +121,6 @@ public class WeChatDllController {
return TResponse.ok(ResponseCodeEnum.SUCCESS, list); 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") @ApiOperation(value = "发送消息汇总入口", notes = "sendMsgMaster")
@PostMapping(value = "/send/msgMaster") @PostMapping(value = "/send/msgMaster")
public TResponse<WxPpWcfSendTextMsgResp> sendMsgMaster(@Validated @RequestBody String jsonString) { public TResponse<WxPpWcfSendTextMsgResp> sendMsgMaster(@Validated @RequestBody String jsonString) {
@ -184,12 +183,12 @@ public class WeChatDllController {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list); // return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// } // }
// @ApiOperation(value = "撤回消息", notes = "queryMsgTypeList") @ApiOperation(value = "撤回消息", notes = "revokeMsg")
// @PostMapping(value = "/list/msgType") @PostMapping(value = "/revokeMsg")
// public TResponse<Object> queryMsgTypeList() { public TResponse<Object> revokeMsg(@Validated @RequestBody WxPpWcfRevokeMsgReq request) {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list); return TResponse.ok(ResponseCodeEnum.SUCCESS);
// } }
//
// @ApiOperation(value = "转发消息", notes = "queryMsgTypeList") // @ApiOperation(value = "转发消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType") // @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() { // public TResponse<Object> queryMsgTypeList() {
@ -209,19 +208,55 @@ public class WeChatDllController {
// } // }
// //
// @ApiOperation(value = "通过好友申请", notes = "queryMsgTypeList") @ApiOperation(value = "通过好友申请", notes = "passFriendApply")
// @PostMapping(value = "/list/msgType") @PostMapping(value = "/passFriendApply")
// public TResponse<WxPpSendPatOnePatMsgResp> friendApply(@Validated @RequestBody WxPpPatOnePatMsgReq request) { public TResponse<Object> passFriendApply(@Validated @RequestBody WxPpWcfPassFriendApplyReq request) {
// // WxPpSendPatOnePatMsgResp resp = weChatDllService.patOnePat(request); weChatDllService.passFriendApply(request);
// return TResponse.ok(ResponseCodeEnum.SUCCESS, resp); return TResponse.ok(ResponseCodeEnum.SUCCESS);
// } }
@ApiOperation(value = "添加群成员为微信好友", notes = "addFriendGroupMember")
@PostMapping(value = "/addFriend/groupMember")
public TResponse<Object> addFriendGroupMember(@Validated @RequestBody WxPpWcfAddFriendGroupMemberReq request) {
weChatDllService.addFriendGroupMember(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "查询群成员列表", notes = "queryGroupMemberList")
@PostMapping(value = "/groupMember/list")
public TResponse<List<WxPpWcfGroupMemberResp>> queryGroupMemberList(@Validated @RequestBody WxPpWcfGroupMemberReq request) {
List<WxPpWcfGroupMemberResp> list = weChatDllService.queryGroupMemberList(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
}
@ApiOperation(value = "邀请群成员", notes = "inviteGroupMember")
@PostMapping(value = "/groupMember/invite")
public TResponse<Object> inviteGroupMember(@Validated @RequestBody WxPpWcfInviteGroupMemberReq request) {
weChatDllService.inviteGroupMember(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "删除群成员", notes = "deleteGroupMember")
@PostMapping(value = "/groupMember/delete")
public TResponse<Object> deleteGroupMember(@Validated @RequestBody WxPpWcfDeleteGroupMemberReq request) {
weChatDllService.deleteGroupMember(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "查询朋友圈", notes = "queryFriendCircle")
@PostMapping(value = "/friendCircle")
public TResponse<Object> queryFriendCircle() {
weChatDllService.queryFriendCircle();
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
@ApiOperation(value = "接收转账", notes = "receiveTransfer")
@PostMapping(value = "/receiveTransfer")
public TResponse<Object> receiveTransfer(@Validated @RequestBody WxPpWcfReceiveTransferReq request) {
weChatDllService.receiveTransfer(request);
return TResponse.ok(ResponseCodeEnum.SUCCESS);
}
// @ApiOperation(value = "获取朋友圈消息", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
// }
//
// @ApiOperation(value = "下载图片、视频、文件", notes = "queryMsgTypeList") // @ApiOperation(value = "下载图片、视频、文件", notes = "queryMsgTypeList")
// @PostMapping(value = "/list/msgType") // @PostMapping(value = "/list/msgType")
// public TResponse<Object> queryMsgTypeList() { // public TResponse<Object> queryMsgTypeList() {
@ -233,23 +268,5 @@ public class WeChatDllController {
// public TResponse<Object> queryMsgTypeList() { // public TResponse<Object> queryMsgTypeList() {
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list); // 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

@ -7,7 +7,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
/** /**
* DTO-ge微信消息 * DTO-微信消息
* *
* @author chandler * @author chandler
* @date 2024-09-26 19:56 * @date 2024-09-26 19:56

View File

@ -0,0 +1,35 @@
package com.wechat.ferry.entity.po.wcf;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 实体类-群信息表
*
* @author chandler
* @date 2024-12-27 10:01
*/
@Data
@ApiModel(value = "ChatRoom", description = "群信息表")
public class ChatRoom {
/**
* 群名称
*/
@ApiModelProperty(value = "群名称")
private String chatRoomName;
/**
* 用户名称列表
*/
@ApiModelProperty(value = "用户名称列表")
private String userNameList;
/**
* 显示名称列表
*/
@ApiModelProperty(value = "显示名称列表")
private String displayNameList;
}

View File

@ -0,0 +1,47 @@
package com.wechat.ferry.entity.po.wcf;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 实体类-群详细信息表
*
* @author chandler
* @date 2024-12-27 10:03
*/
@Data
@ApiModel(value = "ChatRoomInfo", description = "群详细信息表")
public class ChatRoomInfo {
/**
* 群名称
*/
@ApiModelProperty(value = "群名称")
private String chatRoomName;
/**
* 群公告
*/
@ApiModelProperty(value = "群公告")
private String announcement;
/**
* 公告编辑者
*/
@ApiModelProperty(value = "公告编辑者")
private String announcementEditor;
/**
* 公告发布时间
*/
@ApiModelProperty(value = "公告发布时间")
private String announcementPublishTime;
/**
* 群状态
*/
@ApiModelProperty(value = "群状态")
private Integer chatRoomStatus;
}

View File

@ -0,0 +1,71 @@
package com.wechat.ferry.entity.po.wcf;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 实体类-联系人表
*
* @author chandler
* @date 2024-12-27 09:47
*/
@Data
@ApiModel(value = "Contact对象", description = "联系人表")
public class Contact {
/**
* 用户名
*/
@ApiModelProperty(value = "用户名")
private String userName;
/**
* 别名
*/
@ApiModelProperty(value = "别名")
private String alias;
/**
* 昵称
*/
@ApiModelProperty(value = "昵称")
private String nickname;
/**
* 删除标志
*/
@ApiModelProperty(value = "删除标志")
private Integer delFlag;
/**
* 验证标志
*/
@ApiModelProperty(value = "验证标志")
private Integer verifyFlag;
/**
* 备注
*/
@ApiModelProperty(value = "备注")
private String remark;
/**
* 标签ID列表
*/
@ApiModelProperty(value = "标签ID列表")
private String labelIdList;
/**
* 域名列表
*/
@ApiModelProperty(value = "域名列表")
private String domainList;
/**
* 群类型
*/
@ApiModelProperty(value = "群类型")
private Integer chatRoomType;
}

View File

@ -0,0 +1,35 @@
package com.wechat.ferry.entity.po.wcf;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 实体类-联系人头像信息表
*
* @author chandler
* @date 2024-12-27 09:59
*/
@Data
@ApiModel(value = "ContactHeadImgUrl对象", description = "联系人头像信息表")
public class ContactHeadImgUrl {
/**
* 用户名
*/
@ApiModelProperty(value = "用户名")
private String userName;
/**
* 小头像URL
*/
@ApiModelProperty(value = "小头像URL")
private String smallHeadIngUrl;
/**
* 大头像URL
*/
@ApiModelProperty(value = "大头像URL")
private String bigHeadIngUrl;
}

View File

@ -0,0 +1,35 @@
package com.wechat.ferry.entity.po.wcf;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 实体类-撤回消息存储表
*
* @author chandler
* @date 2024-12-27 10:08
*/
@Data
@ApiModel(value = "RevokeMsgStorage对象", description = "撤回消息存储表")
public class RevokeMsgStorage {
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private Integer createTime;
/**
* 消息服务ID
*/
@ApiModelProperty(value = "消息服务ID")
private Integer msgSvrId;
/**
* 撤回服务ID
*/
@ApiModelProperty(value = "撤回服务ID")
private Integer revokeSvrId;
}

View File

@ -0,0 +1,34 @@
package com.wechat.ferry.entity.vo.request;
import javax.validation.constraints.NotBlank;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* 请求入参-添加群成员为好友
*
* @author chandler
* @date 2024-12-25 09:53
*/
@Data
@ApiModel(value = "wxPpWcfAddFriendGroupMemberReq", description = "个微WCF添加群成员为好友请求入参")
public class WxPpWcfAddFriendGroupMemberReq {
/**
* 群编号
*/
@NotBlank(message = "群编号不能为空")
@ApiModelProperty(value = "群编号")
private String groupNo;
/**
* 待添加的群成员列表
*/
@ApiModelProperty(value = "待添加的群成员列表")
private List<String> groupMembers;
}

View File

@ -0,0 +1,34 @@
package com.wechat.ferry.entity.vo.request;
import java.util.List;
import javax.validation.constraints.NotBlank;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微WCF删除群成员
*
* @author chandler
* @date 2024-12-25 10:01
*/
@Data
@ApiModel(value = "wxPpWcfDeleteGroupMemberReq", description = "个微WCF删除群成员请求入参")
public class WxPpWcfDeleteGroupMemberReq {
/**
* 群编号
*/
@NotBlank(message = "群编号不能为空")
@ApiModelProperty(value = "群编号")
private String groupNo;
/**
* 待删除的群成员列表
*/
@ApiModelProperty(value = "待删除的群成员列表")
private List<String> groupMembers;
}

View File

@ -0,0 +1,34 @@
package com.wechat.ferry.entity.vo.request;
import java.util.List;
import javax.validation.constraints.NotBlank;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-个微WCF邀请群成员
*
* @author chandler
* @date 2024-12-25 09:51
*/
@Data
@ApiModel(value = "wxPpWcfInviteGroupMemberReq", description = "个微WCF邀请群成员请求入参")
public class WxPpWcfInviteGroupMemberReq {
/**
* 群编号
*/
@NotBlank(message = "群编号不能为空")
@ApiModelProperty(value = "群编号")
private String groupNo;
/**
* 待邀请的群成员列表
*/
@ApiModelProperty(value = "待邀请的群成员列表")
private List<String> groupMembers;
}

View File

@ -0,0 +1,45 @@
package com.wechat.ferry.entity.vo.request;
import javax.validation.constraints.NotBlank;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-通过好友申请
*
* @author chandler
* @date 2024-12-25 09:30
*/
@Data
@ApiModel(value = "wxPpWcfPassFriendApplyReq", description = "个微WCF通过好友申请请求入参")
public class WxPpWcfPassFriendApplyReq {
/**
* 申请人
* v3 xml.attrib["encryptusername"]
* 加密用户名 (好友申请消息里 v3 开头的字符串)
*/
@NotBlank(message = "申请人不能为空")
@ApiModelProperty(value = "申请人")
private String applicant;
/**
* 审核人
* v4 xml.attrib["ticket"]
* Ticket (好友申请消息里 v4 开头的字符串)
* 一般指自己别人申请添加自己审核是否通过
*/
@NotBlank(message = "审核人不能为空")
@ApiModelProperty(value = "审核人")
private String reviewer;
/**
* 场景
* 申请方式 (好友申请消息里的 scene); 为了兼容旧接口默认为扫码添加 (30)
*/
@ApiModelProperty(value = "场景")
private String scene;
}

View File

@ -0,0 +1,35 @@
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-12-25 13:46
*/
@Data
@ApiModel(value = "wxPpWcfReceiveTransferReq", description = "个微WCF接收转账请求入参")
public class WxPpWcfReceiveTransferReq {
/**
* 转账人
*/
@ApiModelProperty(value = "转账人")
private String weChatUid;
/**
* 转账编号 transferId
*/
@ApiModelProperty(value = "转账编号")
private String transferId;
/**
* 交易编号 Transaction id
*/
@ApiModelProperty(value = "交易编号")
private String transactionId;
}

View File

@ -0,0 +1,25 @@
package com.wechat.ferry.entity.vo.request;
import javax.validation.constraints.NotBlank;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 请求入参-撤回消息
*
* @author chandler
* @date 2024-12-25 12:00
*/
@Data
@ApiModel(value = "wxPpWcfRevokeMsgReq", description = "个微WCF撤回消息请求入参")
public class WxPpWcfRevokeMsgReq {
/**
* 消息编号
*/
@ApiModelProperty(value = "场景")
private String msgId;
}

View File

@ -33,4 +33,16 @@ public class WxPpWcfGroupMemberResp {
@ApiModelProperty(value = "状态") @ApiModelProperty(value = "状态")
private String state; private String state;
/**
* 是否自己
*/
@ApiModelProperty(value = "是否自己")
private Boolean whetherSelf;
/**
* 是否企微
*/
@ApiModelProperty(value = "是否企微")
private Boolean whetherWork;
} }

View File

@ -8,15 +8,15 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 枚举-消息转发开关 * 枚举-消息回调开关
* 1-关闭 2-转发 3-发送成功才转发 * 1-关闭 2-部回调 3-发送成功才回调
* *
* @author chandler * @author chandler
* @date 2024/10/01 15:42 * @date 2024/10/01 15:42
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum MsgFwdTypeEnum { public enum MsgCallbackTypeEnum {
/** /**
* 1-关闭 * 1-关闭
@ -24,14 +24,14 @@ public enum MsgFwdTypeEnum {
CLOSE("1", "关闭"), CLOSE("1", "关闭"),
/** /**
* 2-转发 * 2-部回调
*/ */
ALL("2", "转发"), ALL("2", "部回调"),
/** /**
* 3-发送成功才转发 * 3-发送成功才回调
*/ */
SUCCESS("3", "发送成功才转发"), SUCCESS("3", "发送成功才回调"),
/** /**
* 未匹配上 * 未匹配上
@ -47,12 +47,13 @@ public enum MsgFwdTypeEnum {
/** /**
* map集合 keycode val枚举 * map集合 keycode val枚举
*/ */
public static final Map<String, MsgFwdTypeEnum> codeMap = Arrays.stream(values()).collect(Collectors.toMap(MsgFwdTypeEnum::getCode, v -> v)); public static final Map<String, MsgCallbackTypeEnum> codeMap =
Arrays.stream(values()).collect(Collectors.toMap(MsgCallbackTypeEnum::getCode, v -> v));
/** /**
* 根据code获取枚举 * 根据code获取枚举
*/ */
public static MsgFwdTypeEnum getCodeMap(String code) { public static MsgCallbackTypeEnum getCodeMap(String code) {
return codeMap.getOrDefault(code, UN_MATCH); return codeMap.getOrDefault(code, UN_MATCH);
} }

View File

@ -0,0 +1,48 @@
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/12/27 18:09
*/
@Getter
@AllArgsConstructor
public enum MsgEventTypeEnum {
/**
* 1-注入成功
*/
INJECT("1", "注入成功"),
/**
* 未匹配上
*/
UN_MATCH("", null),
// 结束
;
private final String code;
private final String name;
/**
* map集合 keycode val枚举
*/
public static final Map<String, MsgEventTypeEnum> codeMap = Arrays.stream(values()).collect(Collectors.toMap(MsgEventTypeEnum::getCode, v -> v));
/**
* 根据code获取枚举
*/
public static MsgEventTypeEnum getCodeMap(String code) {
return codeMap.getOrDefault(code, UN_MATCH);
}
}

View File

@ -0,0 +1,49 @@
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/12/25 14:15
*/
@Getter
@AllArgsConstructor
public enum ReceiveMsgChannelEnum {
/**
* 1-签到
*/
SIGN_IN("1", "签到"),
/**
* 未匹配上
*/
UN_MATCH("", null),
// 结束
;
private final String code;
private final String name;
/**
* map集合 keycode val枚举
*/
public static final Map<String, ReceiveMsgChannelEnum> codeMap =
Arrays.stream(values()).collect(Collectors.toMap(ReceiveMsgChannelEnum::getCode, v -> v));
/**
* 根据code获取枚举
*/
public static ReceiveMsgChannelEnum getCodeMap(String code) {
return codeMap.getOrDefault(code, UN_MATCH);
}
}

View File

@ -1,25 +1,29 @@
package com.wechat.ferry.handle; package com.wechat.ferry.handle;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.function.Function;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.google.protobuf.ByteString;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.wechat.ferry.entity.dto.WxPpMsgDTO; import com.wechat.ferry.entity.dto.WxPpMsgDTO;
import com.wechat.ferry.entity.proto.Wcf.DbQuery; import com.wechat.ferry.entity.proto.Wcf.DbQuery;
import com.wechat.ferry.entity.proto.Wcf.DbRow; import com.wechat.ferry.entity.proto.Wcf.DbRow;
import com.wechat.ferry.entity.proto.Wcf.DecPath; import com.wechat.ferry.entity.proto.Wcf.DecPath;
import com.wechat.ferry.entity.proto.Wcf.Functions; 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.Request;
import com.wechat.ferry.entity.proto.Wcf.Response; import com.wechat.ferry.entity.proto.Wcf.Response;
import com.wechat.ferry.entity.proto.Wcf.Verification;
import com.wechat.ferry.entity.proto.Wcf.WxMsg; import com.wechat.ferry.entity.proto.Wcf.WxMsg;
import com.wechat.ferry.exception.BizException;
import com.wechat.ferry.service.SDK; import com.wechat.ferry.service.SDK;
import com.wechat.ferry.utils.HttpClientUtil; import com.wechat.ferry.utils.HttpClientUtil;
import com.wechat.ferry.utils.XmlJsonConvertUtil; import com.wechat.ferry.utils.XmlJsonConvertUtil;
@ -117,14 +121,21 @@ public class WeChatSocketClient {
public Response sendCmd(Request req) { public Response sendCmd(Request req) {
try { try {
// 设置超时时间 20s
cmdSocket.setSendTimeout(20000);
ByteBuffer bb = ByteBuffer.wrap(req.toByteArray()); ByteBuffer bb = ByteBuffer.wrap(req.toByteArray());
cmdSocket.send(bb); cmdSocket.send(bb);
ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE); ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE);
long size = cmdSocket.receive(ret, true); long size = cmdSocket.receive(ret, true);
return Response.parseFrom(Arrays.copyOfRange(ret.array(), 0, (int)size)); return Response.parseFrom(Arrays.copyOfRange(ret.array(), 0, (int)size));
} catch (Exception e) { } catch (Exception e) {
if ("Timed out".equals(e.getMessage())) {
log.error("请求超时: ", e);
throw new BizException("请求超时:1.接口耗时太长2.服务与客户端失去联系,请重启本服务!详细异常信息:" + e.getMessage());
} else {
log.error("命令调用失败: ", e); log.error("命令调用失败: ", e);
return null; throw new BizException("命令调用失败:" + e.getMessage());
}
} }
} }
@ -159,42 +170,6 @@ public class WeChatSocketClient {
return null; return null;
} }
/**
* 接收好友请求
*
* @param v3 xml.attrib["encryptusername"]
* @param v4 xml.attrib["ticket"]
* @return 结果状态码
*/
public int acceptNewFriend(String v3, String v4) {
int ret = -1;
Verification verification = Verification.newBuilder().setV3(v3).setV4(v4).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ACCEPT_FRIEND_VALUE).setV(verification).build();
Response rsp = sendCmd(req);
if (rsp != null) {
ret = rsp.getStatus();
}
return ret;
}
/**
* 添加群成员为微信好友
*
* @param roomID 群ID
* @param wxIds 要加群的人列表逗号分隔
* @return 1 为成功其他失败
*/
public int addChatroomMembers(String roomID, String wxIds) {
int ret = -1;
MemberMgmt memberMgmt = MemberMgmt.newBuilder().setRoomid(roomID).setWxids(wxIds).build();
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_ADD_ROOM_MEMBERS_VALUE).setM(memberMgmt).build();
Response rsp = sendCmd(req);
if (rsp != null) {
ret = rsp.getStatus();
}
return ret;
}
/** /**
* 解密图片 * 解密图片
* *
@ -348,7 +323,16 @@ public class WeChatSocketClient {
} }
} }
public void forwardMsg(WxMsg msg, String url) { /**
* 本机回调解析消息
*
* @param msg 消息内容
* @param url 回调地址
*
* @author chandler
* @date 2024-10-05 12:50
*/
public void localCallbackAnalyzeMsg(WxMsg msg, String url) {
String xml = msg.getXml(); String xml = msg.getXml();
xml = xml.replaceAll(">[\\s\\p{Zs}]*<", "><"); xml = xml.replaceAll(">[\\s\\p{Zs}]*<", "><");
String content = msg.getContent(); String content = msg.getContent();
@ -396,10 +380,50 @@ public class WeChatSocketClient {
try { try {
String responseStr = HttpClientUtil.doPostJson(url, jsonString); String responseStr = HttpClientUtil.doPostJson(url, jsonString);
if (!JSONObject.parseObject(responseStr).getString("code").equals("200")) { if (!JSONObject.parseObject(responseStr).getString("code").equals("200")) {
log.error("本机消息转发失败!-URL{}", url); log.error("本机消息回调失败!-URL{}", url);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("转发接口报错:", e); log.error("本机消息回调接口报错:", e);
}
}
/**
* 获取SQL类型
*
* @param type 转换类型
* @return 函数
*
* @author chandler
* @date 2024-10-05 12:54
*/
public Function<byte[], Object> getSqlType(int type) {
Map<Integer, Function<byte[], Object>> sqlTypeMap = new HashMap<>();
// 初始化SQL_TYPES 根据类型执行不同的Func
sqlTypeMap.put(1, bytes -> new String(bytes, StandardCharsets.UTF_8));
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);
}
/**
* SQL转换类型
*
* @param type 转换类型
* @param content 待转换内容
*
* @author chandler
* @date 2024-10-05 12:54
*/
public Object convertSqlVal(int type, ByteString content) {
// 根据每一列的类型转换
Function<byte[], Object> converter = getSqlType(type);
if (converter != null) {
return converter.apply(content.toByteArray());
} else {
log.warn("[SQL转换类型]-未知的SQL类型: {}", type);
return content.toByteArray();
} }
} }

View File

@ -2,10 +2,16 @@ package com.wechat.ferry.service;
import java.util.List; import java.util.List;
import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfReceiveTransferReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfRevokeMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
@ -117,17 +123,6 @@ public interface WeChatDllService {
*/ */
List<WxPpWcfDatabaseRowResp> execDbQuerySql(WxPpWcfDatabaseSqlReq request); List<WxPpWcfDatabaseRowResp> execDbQuerySql(WxPpWcfDatabaseSqlReq request);
/**
* 查询群成员
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 20:59
*/
List<WxPpWcfGroupMemberResp> queryGroupMember(WxPpWcfGroupMemberReq request);
/** /**
* 发送文本消息 @ * 发送文本消息 @
* *
@ -207,4 +202,90 @@ public interface WeChatDllService {
*/ */
WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request); WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request);
/**
* 撤回消息
*
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 11:59
*/
String revokeMsg(WxPpWcfRevokeMsgReq request);
/**
* 通过好友申请
*
* @param request 请求入参
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 09:38
*/
String passFriendApply(WxPpWcfPassFriendApplyReq request);
/**
* 添加群成员为微信好友
*
* @param request 请求入参
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 09:38
*/
String addFriendGroupMember(WxPpWcfAddFriendGroupMemberReq request);
/**
* 查询群成员列表
*
* @param request 请求入参
* @return 数据库记录
*
* @author chandler
* @date 2024-10-02 20:59
*/
List<WxPpWcfGroupMemberResp> queryGroupMemberList(WxPpWcfGroupMemberReq request);
/**
* 邀请群成员
*
* @param request 请求入参
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 10:02
*/
String inviteGroupMember(WxPpWcfInviteGroupMemberReq request);
/**
* 删除群成员
*
* @param request 请求入参
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 10:03
*/
String deleteGroupMember(WxPpWcfDeleteGroupMemberReq request);
/**
* 查询朋友圈
*
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 11:11
*/
String queryFriendCircle();
/**
* 接收转账
*
* @param request 请求入参
* @return 结果状态
*
* @author chandler
* @date 2024-12-25 13:48
*/
String receiveTransfer(WxPpWcfReceiveTransferReq request);
} }

View File

@ -1,12 +1,9 @@
package com.wechat.ferry.service.impl; package com.wechat.ferry.service.impl;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -17,14 +14,20 @@ import org.springframework.util.ObjectUtils;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.InvalidProtocolBufferException;
import com.wechat.ferry.aggregation.facade.ContactDo;
import com.wechat.ferry.config.WeChatFerryProperties; import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.entity.proto.Wcf; import com.wechat.ferry.entity.proto.Wcf;
import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPatOnePatMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfReceiveTransferReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfRevokeMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendEmojiMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendFileMsgReq;
import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq; import com.wechat.ferry.entity.vo.request.WxPpWcfSendImageMsgReq;
@ -46,11 +49,10 @@ import com.wechat.ferry.entity.vo.response.WxPpWcfSendRichTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp; import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp;
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp; import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
import com.wechat.ferry.enums.DatabaseNameEnum; import com.wechat.ferry.enums.DatabaseNameEnum;
import com.wechat.ferry.enums.MsgFwdTypeEnum; import com.wechat.ferry.enums.MsgCallbackTypeEnum;
import com.wechat.ferry.enums.SexEnum; import com.wechat.ferry.enums.SexEnum;
import com.wechat.ferry.enums.WxContactsMixedEnum;
import com.wechat.ferry.enums.WxContactsOfficialEnum;
import com.wechat.ferry.enums.WxContactsTypeEnum; import com.wechat.ferry.enums.WxContactsTypeEnum;
import com.wechat.ferry.exception.BizException;
import com.wechat.ferry.handle.WeChatSocketClient; import com.wechat.ferry.handle.WeChatSocketClient;
import com.wechat.ferry.service.WeChatDllService; import com.wechat.ferry.service.WeChatDllService;
import com.wechat.ferry.utils.HttpClientUtil; import com.wechat.ferry.utils.HttpClientUtil;
@ -80,6 +82,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public Boolean loginStatus() { public Boolean loginStatus() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
Boolean status = wechatSocketClient.isLogin(); Boolean status = wechatSocketClient.isLogin();
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
log.info("[查询]-[登录状态]-耗时:{}msstatus:{}", (endTime - startTime), status); log.info("[查询]-[登录状态]-耗时:{}msstatus:{}", (endTime - startTime), status);
@ -89,6 +93,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public String queryLoginWeChatUid() { public String queryLoginWeChatUid() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
String weChatUid = ""; String weChatUid = "";
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_SELF_WXID_VALUE).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_SELF_WXID_VALUE).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
@ -103,6 +109,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public WxPpWcfLoginInfoResp queryLoginWeChatInfo() { public WxPpWcfLoginInfoResp queryLoginWeChatInfo() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
WxPpWcfLoginInfoResp resp = new WxPpWcfLoginInfoResp(); WxPpWcfLoginInfoResp resp = new WxPpWcfLoginInfoResp();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_USER_INFO_VALUE).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_USER_INFO_VALUE).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
@ -121,6 +129,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public List<WxPpWcfMsgTypeResp> queryMsgTypeList() { public List<WxPpWcfMsgTypeResp> queryMsgTypeList() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
List<WxPpWcfMsgTypeResp> list = new ArrayList<>(); List<WxPpWcfMsgTypeResp> list = new ArrayList<>();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_MSG_TYPES_VALUE).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_MSG_TYPES_VALUE).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
@ -142,6 +152,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public List<WxPpWcfContactsResp> queryContactsList() { public List<WxPpWcfContactsResp> queryContactsList() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
List<WxPpWcfContactsResp> list = new ArrayList<>(); List<WxPpWcfContactsResp> list = new ArrayList<>();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_CONTACTS_VALUE).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_CONTACTS_VALUE).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
@ -174,38 +186,9 @@ public class WeChatDllServiceImpl implements WeChatDllService {
} }
// 微信类型 // 微信类型
if (!ObjectUtils.isEmpty(rpcContact.getWxid())) { if (!ObjectUtils.isEmpty(rpcContact.getWxid())) {
// 官方杂号集合 String type = ContactDo.convertContactType(rpcContact.getWxid(), weChatFerryProperties);
Map<String, String> mixedNoMap = WxContactsMixedEnum.toCodeNameMap(); vo.setType(type);
mixedNoMap.putAll(convertContactsTypeProperties(weChatFerryProperties.getContactsTypeMixed())); vo.setTypeLabel(WxContactsTypeEnum.getCodeMap(rpcContact.getWxid()).getName());
// 公众号
Map<String, String> officialMap = WxContactsOfficialEnum.toCodeNameMap();
officialMap.putAll(convertContactsTypeProperties(weChatFerryProperties.getContactsTypeOfficial()));
// 类型判断,存在优先级的官方杂号优先级高于微信公众号(如果定义重复了常规禁止重复手机端和电脑端分类不同)
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 (mixedNoMap.containsKey(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 (officialMap.containsKey(rpcContact.getWxid())) {
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); list.add(vo);
} }
@ -218,6 +201,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public List<String> queryDbTableNameList() { public List<String> queryDbTableNameList() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_DB_NAMES_VALUE).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_DB_NAMES_VALUE).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
@ -232,6 +217,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public List<WxPpWcfDatabaseTableResp> queryDbTableList(WxPpWcfDatabaseTableReq request) { public List<WxPpWcfDatabaseTableResp> queryDbTableList(WxPpWcfDatabaseTableReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[查询]-[数据库表列表]-request:{}", request); log.info("[查询]-[数据库表列表]-request:{}", request);
List<WxPpWcfDatabaseTableResp> list = new ArrayList<>(); List<WxPpWcfDatabaseTableResp> list = new ArrayList<>();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_DB_TABLES_VALUE).setStr(request.getDatabaseName()).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_GET_DB_TABLES_VALUE).setStr(request.getDatabaseName()).build();
@ -253,6 +240,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
@Override @Override
public List<WxPpWcfDatabaseRowResp> execDbQuerySql(WxPpWcfDatabaseSqlReq request) { public List<WxPpWcfDatabaseRowResp> execDbQuerySql(WxPpWcfDatabaseSqlReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
List<WxPpWcfDatabaseRowResp> list = new ArrayList<>(); List<WxPpWcfDatabaseRowResp> list = new ArrayList<>();
List<Wcf.DbRow> wcfList = wechatSocketClient.querySql(request.getDatabaseName(), request.getSqlText()); List<Wcf.DbRow> wcfList = wechatSocketClient.querySql(request.getDatabaseName(), request.getSqlText());
if (!CollectionUtils.isEmpty(wcfList)) { if (!CollectionUtils.isEmpty(wcfList)) {
@ -266,7 +255,7 @@ public class WeChatDllServiceImpl implements WeChatDllService {
log.warn("未知的SQL类型: {}", dbField.getType()); log.warn("未知的SQL类型: {}", dbField.getType());
value = dbField.getContent().toByteArray(); value = dbField.getContent().toByteArray();
} else { } else {
value = convertSqlVal(dbField.getType(), dbField.getContent()); value = wechatSocketClient.convertSqlVal(dbField.getType(), dbField.getContent());
} }
fieldVo.setType(String.valueOf(dbField.getType())); fieldVo.setType(String.valueOf(dbField.getType()));
fieldVo.setColumn(dbField.getColumn()); fieldVo.setColumn(dbField.getColumn());
@ -283,9 +272,219 @@ public class WeChatDllServiceImpl implements WeChatDllService {
} }
@Override @Override
public List<WxPpWcfGroupMemberResp> queryGroupMember(WxPpWcfGroupMemberReq request) { public WxPpWcfSendTextMsgResp sendTextMsg(WxPpWcfSendTextMsgReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[文本消息]-入参打印:{}", request);
String atUser = "";
if (request.getIsAtAll()) {
// 艾特全体仅管理员有效
atUser = "@all";
} else {
// 处理艾特的人员
if (!CollectionUtils.isEmpty(request.getAtUsers())) {
atUser = String.join(",", request.getAtUsers());
}
}
Wcf.TextMsg textMsg = Wcf.TextMsg.newBuilder().setMsg(request.getMsgText()).setReceiver(request.getRecipient()).setAters(atUser).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_TXT_VALUE).setTxt(textMsg).build();
log.debug("sendText: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
// 0 为成功其他失败
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[文本消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendRichTextMsgResp sendRichTextMsg(WxPpWcfSendRichTextMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[富文本消息]-入参打印:{}", 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 req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_RICH_TXT_VALUE).setRt(richTextMsg).build();
log.debug("sendRichText: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[富文本消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendXmlMsgResp sendXmlMsg(WxPpWcfSendXmlMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[XML消息]-入参打印:{}", request);
int xmlType = 0x21;
if ("21".equals(request.getXmlType())) {
// 小程序
xmlType = 0x21;
} else {
xmlType = Integer.parseInt(request.getXmlType());
}
Wcf.XmlMsg xmlMsg = Wcf.XmlMsg.newBuilder().setContent(request.getXmlContent()).setReceiver(request.getRecipient())
.setPath(request.getResourcePath()).setType(xmlType).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_XML_VALUE).setXml(xmlMsg).build();
log.debug("sendXml: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[XML消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendImageMsgResp sendImageMsg(WxPpWcfSendImageMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[图片消息]-入参打印:{}", request);
WxPpWcfSendImageMsgResp resp = new WxPpWcfSendImageMsgResp();
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_IMG_VALUE).setFile(pathMsg).build();
log.debug("sendImage: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[图片消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendEmojiMsgResp sendEmojiMsg(WxPpWcfSendEmojiMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[表情消息]-入参打印:{}", request);
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_EMOTION_VALUE).setFile(pathMsg).build();
log.debug("sendEmotion: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[表情消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendFileMsgResp sendFileMsg(WxPpWcfSendFileMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[文件消息]-入参打印:{}", request);
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_FILE_VALUE).setFile(pathMsg).build();
log.debug("sendFile: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[文件消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[发送消息]-[拍一拍消息]-入参打印:{}", 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);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[拍一拍消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public String revokeMsg(WxPpWcfRevokeMsgReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[撤回消息]-[消息撤回]-入参打印:{}", request);
long msgId = Long.parseLong(request.getMsgId());
Wcf.Request wcfReq = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_REVOKE_MSG_VALUE).setUi64(msgId).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(wcfReq);
int state = judgeWcfCmdState(rsp);
// 回调处理
String stringJson = JSON.toJSONString(request);
sendMsgCallback(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[撤回消息]-[消息撤回]-处理结束,耗时:{}ms", (endTime - startTime));
return "";
}
@Override
public String passFriendApply(WxPpWcfPassFriendApplyReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[好友申请]-[通过好友申请]-入参打印:{}", request);
Wcf.Verification verification = Wcf.Verification.newBuilder().setV3(request.getApplicant()).setV4(request.getReviewer()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_ACCEPT_FRIEND_VALUE).setV(verification).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
long endTime = System.currentTimeMillis();
log.info("[好友申请]-[通过好友申请]-处理结束,耗时:{}ms", (endTime - startTime));
return "";
}
@Override
public String addFriendGroupMember(WxPpWcfAddFriendGroupMemberReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
log.info("[添加好友]-[添加群成员为好友]-入参打印:{}", request);
if (CollectionUtils.isEmpty(request.getGroupMembers())) {
log.error("[添加好友]-[添加群成员为好友]-待添加人员为空,本次操作取消");
return "";
}
String groupMembersStr = String.join(",", request.getGroupMembers());
Wcf.MemberMgmt memberMgmt = Wcf.MemberMgmt.newBuilder().setRoomid(request.getGroupNo()).setWxids(groupMembersStr).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_ADD_ROOM_MEMBERS_VALUE).setM(memberMgmt).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeWcfCmdState(rsp);
long endTime = System.currentTimeMillis();
log.info("[添加好友]-[添加群成员为好友]-处理结束,耗时:{}ms", (endTime - startTime));
return "";
}
@Override
public List<WxPpWcfGroupMemberResp> queryGroupMemberList(WxPpWcfGroupMemberReq request) {
long startTime = System.currentTimeMillis();
// 公共校验
checkClientStatus();
List<WxPpWcfGroupMemberResp> list = new ArrayList<>(); List<WxPpWcfGroupMemberResp> list = new ArrayList<>();
String weChatUid = queryLoginWeChatUid();
// 查询群成员 // 查询群成员
List<Wcf.DbRow> wcfList = new ArrayList<>(); List<Wcf.DbRow> wcfList = new ArrayList<>();
if (!ObjectUtils.isEmpty(request.getGroupNo())) { if (!ObjectUtils.isEmpty(request.getGroupNo())) {
@ -303,11 +502,11 @@ public class WeChatDllServiceImpl implements WeChatDllService {
for (Wcf.DbField dbField : dbFieldList) { for (Wcf.DbField dbField : dbFieldList) {
if ("UserName".equals(dbField.getColumn())) { if ("UserName".equals(dbField.getColumn())) {
vo = new WxPpWcfGroupMemberResp(); vo = new WxPpWcfGroupMemberResp();
String content = (String)convertSqlVal(dbField.getType(), dbField.getContent()); String content = (String)wechatSocketClient.convertSqlVal(dbField.getType(), dbField.getContent());
vo.setWeChatUid(content); vo.setWeChatUid(content);
} }
if ("NickName".equals(dbField.getColumn())) { if ("NickName".equals(dbField.getColumn())) {
String content = (String)convertSqlVal(dbField.getType(), dbField.getContent()); String content = (String)wechatSocketClient.convertSqlVal(dbField.getType(), dbField.getContent());
vo.setGroupNickName(content); vo.setGroupNickName(content);
dbMap.put(vo.getWeChatUid(), vo.getGroupNickName()); dbMap.put(vo.getWeChatUid(), vo.getGroupNickName());
} }
@ -330,6 +529,10 @@ public class WeChatDllServiceImpl implements WeChatDllService {
for (Wcf.RoomData.RoomMember member : roomData.getMembersList()) { for (Wcf.RoomData.RoomMember member : roomData.getMembersList()) {
vo = new WxPpWcfGroupMemberResp(); vo = new WxPpWcfGroupMemberResp();
vo.setWeChatUid(member.getWxid()); vo.setWeChatUid(member.getWxid());
// 是否为自己微信
vo.setWhetherSelf(weChatUid.equals(member.getWxid()));
// 是否为企微
vo.setWhetherWork(member.getWxid().endsWith(WxContactsTypeEnum.WORK.getAffix()));
String nickName = member.getName(); String nickName = member.getName();
if (ObjectUtils.isEmpty(nickName)) { if (ObjectUtils.isEmpty(nickName)) {
// 如果没有设置群昵称则默认为微信名称 // 如果没有设置群昵称则默认为微信名称
@ -353,207 +556,78 @@ public class WeChatDllServiceImpl implements WeChatDllService {
} }
@Override @Override
public WxPpWcfSendTextMsgResp sendTextMsg(WxPpWcfSendTextMsgReq request) { public String inviteGroupMember(WxPpWcfInviteGroupMemberReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
log.info("[发送消息]-[文本消息]-入参打印:{}", request); // 公共校验
String atUser = ""; checkClientStatus();
if (request.getIsAtAll()) { log.info("[群成员]-[邀请群成员加入]-入参打印:{}", request);
// 艾特全体仅管理员有效 if (CollectionUtils.isEmpty(request.getGroupMembers())) {
atUser = "@all"; log.error("[群成员]-[邀请群成员加入]-待邀请进群的人员为空,本次操作取消");
} else { return "";
// 处理艾特的人员
if (!CollectionUtils.isEmpty(request.getAtUsers())) {
atUser = String.join(",", request.getAtUsers());
} }
} String groupMembersStr = String.join(",", request.getGroupMembers());
Wcf.TextMsg textMsg = Wcf.TextMsg.newBuilder().setMsg(request.getMsgText()).setReceiver(request.getRecipient()).setAters(atUser).build(); Wcf.MemberMgmt memberMgmt = Wcf.MemberMgmt.newBuilder().setRoomid(request.getGroupNo()).setWxids(groupMembersStr).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_TXT_VALUE).setTxt(textMsg).build(); Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_INV_ROOM_MEMBERS_VALUE).setM(memberMgmt).build();
log.debug("sendText: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
// 0 为成功其他失败 int state = judgeWcfCmdState(rsp);
int state = judgeSendMsgState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
log.info("[发送消息]-[文本消息]-处理结束,耗时:{}ms", (endTime - startTime)); log.info("[群成员]-[邀请群成员加入]-处理结束,耗时:{}ms", (endTime - startTime));
return null; return "";
} }
@Override @Override
public WxPpWcfSendRichTextMsgResp sendRichTextMsg(WxPpWcfSendRichTextMsgReq request) { public String deleteGroupMember(WxPpWcfDeleteGroupMemberReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
log.info("[发送消息]-[富文本消息]-入参打印:{}", request); // 公共校验
Wcf.RichText richTextMsg = Wcf.RichText.newBuilder().setName(request.getName()).setAccount(request.getAccount()).setTitle(request.getTitle()) checkClientStatus();
.setDigest(request.getDigest()).setUrl(request.getJumpUrl()).setThumburl(request.getThumbnailUrl()).setReceiver(request.getRecipient()) log.info("[群成员]-[删除群成员]-入参打印:{}", request);
.build(); if (CollectionUtils.isEmpty(request.getGroupMembers())) {
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_RICH_TXT_VALUE).setRt(richTextMsg).build(); log.error("[群成员]-[删除群成员]-待删除的人员为空,本次操作取消");
log.debug("sendRichText: {}", wechatSocketClient.bytesToHex(req.toByteArray())); return "";
}
String groupMembersStr = String.join(",", request.getGroupMembers());
Wcf.MemberMgmt memberMgmt = Wcf.MemberMgmt.newBuilder().setRoomid(request.getGroupNo()).setWxids(groupMembersStr).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DEL_ROOM_MEMBERS_VALUE).setM(memberMgmt).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeSendMsgState(rsp); int state = judgeWcfCmdState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
log.info("[发送消息]-[富文本消息]-处理结束,耗时:{}ms", (endTime - startTime)); log.info("[群成员]-[删除群成员]-处理结束,耗时:{}ms", (endTime - startTime));
return null; return "";
} }
@Override @Override
public WxPpWcfSendXmlMsgResp sendXmlMsg(WxPpWcfSendXmlMsgReq request) { public String queryFriendCircle() {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
log.info("[发送消息]-[XML消息]-入参打印:{}", request); // 公共校验
int xmlType = 0x21; checkClientStatus();
if ("21".equals(request.getXmlType())) { log.info("[查询]-[刷新朋友圈]-开始");
// 小程序 // id 开始 id0 为最新页 (string based uint64)
xmlType = 0x21; Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_REFRESH_PYQ_VALUE).setUi64(0).build();
}
Wcf.XmlMsg xmlMsg = Wcf.XmlMsg.newBuilder().setContent(request.getXmlContent()).setReceiver(request.getRecipient())
.setPath(request.getResourcePath()).setType(xmlType).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_XML_VALUE).setXml(xmlMsg).build();
log.debug("sendXml: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeSendMsgState(rsp); int state = judgeWcfCmdState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
log.info("[发送消息]-[XML消息]-处理结束,耗时:{}ms", (endTime - startTime)); log.info("[查询]-[刷新朋友圈]-处理结束,耗时:{}ms", (endTime - startTime));
return null; return "";
} }
@Override @Override
public WxPpWcfSendImageMsgResp sendImageMsg(WxPpWcfSendImageMsgReq request) { public String receiveTransfer(WxPpWcfReceiveTransferReq request) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
log.info("[发送消息]-[图片消息]-入参打印:{}", request); // 公共校验
WxPpWcfSendImageMsgResp resp = new WxPpWcfSendImageMsgResp(); checkClientStatus();
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build(); log.info("[转账]-[接收转账]-开始");
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_IMG_VALUE).setFile(pathMsg).build(); Wcf.Transfer transfer =
log.debug("sendImage: {}", wechatSocketClient.bytesToHex(req.toByteArray())); Wcf.Transfer.newBuilder().setWxid(request.getWeChatUid()).setTfid(request.getTransferId()).setTaid(request.getTransferId()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_RECV_TRANSFER_VALUE).setTf(transfer).build();
Wcf.Response rsp = wechatSocketClient.sendCmd(req); Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeSendMsgState(rsp); int state = judgeWcfCmdState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
log.info("[发送消息]-[图片消息]-处理结束,耗时:{}ms", (endTime - startTime)); log.info("[转账]-[接收转账]-处理结束,耗时:{}ms", (endTime - startTime));
return null; return "";
}
@Deprecated
@Override
public WxPpWcfSendEmojiMsgResp sendEmojiMsg(WxPpWcfSendEmojiMsgReq request) {
long startTime = System.currentTimeMillis();
log.info("[发送消息]-[表情消息]-入参打印:{}", request);
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_EMOTION_VALUE).setFile(pathMsg).build();
log.debug("sendEmotion: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeSendMsgState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[表情消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendFileMsgResp sendFileMsg(WxPpWcfSendFileMsgReq request) {
long startTime = System.currentTimeMillis();
log.info("[发送消息]-[文件消息]-入参打印:{}", request);
Wcf.PathMsg pathMsg = Wcf.PathMsg.newBuilder().setPath(request.getResourcePath()).setReceiver(request.getRecipient()).build();
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_SEND_FILE_VALUE).setFile(pathMsg).build();
log.debug("sendFile: {}", wechatSocketClient.bytesToHex(req.toByteArray()));
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
int state = judgeSendMsgState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[文件消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
}
@Override
public WxPpWcfSendPatOnePatMsgResp patOnePat(WxPpWcfPatOnePatMsgReq request) {
long startTime = System.currentTimeMillis();
log.info("[发送消息]-[拍一拍消息]-入参打印:{}", 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);
int state = judgeSendMsgState(rsp);
// 转发处理
String stringJson = JSON.toJSONString(request);
sendMsgForward(stringJson, state);
long endTime = System.currentTimeMillis();
log.info("[发送消息]-[拍一拍消息]-处理结束,耗时:{}ms", (endTime - startTime));
return null;
} }
/** /**
* 获取SQL类型 * 消息回调
*
* @param type 转换类型
* @return 函数
*
* @author chandler
* @date 2024-10-05 12:54
*/
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);
}
/**
* SQL转换类型
*
* @param type 转换类型
* @param content 待转换内容
*
* @author chandler
* @date 2024-10-05 12:54
*/
public Object convertSqlVal(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;
}
/**
* 消息转发
* *
* @param jsonString json数据 * @param jsonString json数据
* @param state cmd调用状态 * @param state cmd调用状态
@ -561,27 +635,27 @@ public class WeChatDllServiceImpl implements WeChatDllService {
* @author chandler * @author chandler
* @date 2024-10-10 23:10 * @date 2024-10-10 23:10
*/ */
private void sendMsgForward(String jsonString, Integer state) { private void sendMsgCallback(String jsonString, Integer state) {
// 根据配置文件决定是否转发 // 根据配置文件决定是否回调
if (MsgFwdTypeEnum.CLOSE.getCode().equals(weChatFerryProperties.getSendMsgFwdFlag()) if (MsgCallbackTypeEnum.CLOSE.getCode().equals(weChatFerryProperties.getSendMsgCallbackFlag())
|| (MsgFwdTypeEnum.SUCCESS.getCode().equals(weChatFerryProperties.getSendMsgFwdFlag()) && 0 != state)) { || (MsgCallbackTypeEnum.SUCCESS.getCode().equals(weChatFerryProperties.getSendMsgCallbackFlag()) && 0 != state)) {
// 如果是关闭 或者 配置为成功才转发但发送状态为失败 的情况则取消发送 // 如果是关闭 或者 配置为成功才回调但发送状态为失败 的情况则取消发送
return; return;
} }
// 开启转发且转发地址不为空 // 开启回调且回调地址不为空
if (!CollectionUtils.isEmpty(weChatFerryProperties.getSendMsgFwdUrls())) { if (!CollectionUtils.isEmpty(weChatFerryProperties.getSendMsgCallbackUrls())) {
for (String receiveMsgFwdUrl : weChatFerryProperties.getSendMsgFwdUrls()) { for (String receiveMsgFwdUrl : weChatFerryProperties.getSendMsgCallbackUrls()) {
if (!receiveMsgFwdUrl.startsWith("http")) { if (!receiveMsgFwdUrl.startsWith("http")) {
continue; continue;
} }
try { try {
String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString); String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString);
if (judgeSuccess(responseStr)) { if (judgeSuccess(responseStr)) {
log.error("[发送消息]-消息转发外部接口,获取响应状态失败!-URL{}", receiveMsgFwdUrl); log.error("[发送消息]-消息回调至外部接口,获取响应状态失败!-URL{}", receiveMsgFwdUrl);
} }
log.debug("[发送消息]-[转发接收到的消息]-转发消息至:{}", receiveMsgFwdUrl); log.debug("[发送消息]-[回调接收到的消息]-回调消息至:{}", receiveMsgFwdUrl);
} catch (Exception e) { } catch (Exception e) {
log.error("[发送消息]-消息转发接口[{}]服务异常:", receiveMsgFwdUrl, e); log.error("[发送消息]-消息回调接口[{}]服务异常:", receiveMsgFwdUrl, e);
} }
} }
} }
@ -615,8 +689,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
} }
/** /**
* 判断发送消息状态 * 判断WCF的CMD调用状态
* 0 为成功其他失败 * 有值 为成功其他失败
* *
* @param rsp 响应参数 * @param rsp 响应参数
* @return 状态 * @return 状态
@ -624,8 +698,8 @@ public class WeChatDllServiceImpl implements WeChatDllService {
* @author chandler * @author chandler
* @date 2024-12-23 21:53 * @date 2024-12-23 21:53
*/ */
private int judgeSendMsgState(Wcf.Response rsp) { private int judgeWcfCmdState(Wcf.Response rsp) {
// 0 为成功其他失败 // 有值 为成功其他失败
int state = -1; int state = -1;
if (rsp != null) { if (rsp != null) {
state = rsp.getStatus(); state = rsp.getStatus();
@ -660,4 +734,16 @@ public class WeChatDllServiceImpl implements WeChatDllService {
return map; return map;
} }
/**
* 请求前检测客户端状态
*
* @author chandler
* @date 2025-01-04 18:34
*/
private void checkClientStatus() {
if (!wechatSocketClient.isLogin()) {
throw new BizException("微信客户端未登录或状态异常,请人工关闭本服务之后,退出微信客户端在重启本服务!");
}
}
} }

View File

@ -4,7 +4,6 @@ import java.util.Map;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -14,6 +13,8 @@ import com.alibaba.fastjson2.JSONObject;
import com.wechat.ferry.config.WeChatFerryProperties; import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.entity.dto.WxPpMsgDTO; import com.wechat.ferry.entity.dto.WxPpMsgDTO;
import com.wechat.ferry.service.WeChatMsgService; import com.wechat.ferry.service.WeChatMsgService;
import com.wechat.ferry.strategy.msg.receive.ReceiveMsgFactory;
import com.wechat.ferry.strategy.msg.receive.ReceiveMsgStrategy;
import com.wechat.ferry.utils.HttpClientUtil; import com.wechat.ferry.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -34,34 +35,46 @@ public class WeChatMsgServiceImpl implements WeChatMsgService {
@Override @Override
public void receiveMsg(String jsonString) { public void receiveMsg(String jsonString) {
// 转发接口处理 // 转发接口处理
receiveMsgForward(jsonString); receiveMsgCallback(jsonString);
// 转为JSON对象 // 转为JSON对象
WxPpMsgDTO dto = JSON.parseObject(jsonString, WxPpMsgDTO.class); WxPpMsgDTO dto = JSON.parseObject(jsonString, WxPpMsgDTO.class);
// 有开启的群聊配置 // 有开启的群聊配置
if (!CollectionUtils.isEmpty(weChatFerryProperties.getOpenMsgGroups())) { if (!CollectionUtils.isEmpty(weChatFerryProperties.getOpenMsgGroups())) {
// 指定处理的群聊 // 指定处理的群聊
if (weChatFerryProperties.getOpenMsgGroups().contains(dto.getRoomId())) { if (weChatFerryProperties.getOpenMsgGroups().contains(dto.getRoomId()) || weChatFerryProperties.getOpenMsgGroups().contains("ALL")) {
// TODO 这里可以拓展自己需要的功能 // TODO 模式有多种 1-根据消息类型单独调用某一个 2-全部调用各业务类中自己决定是否继续
if (true) {
// 因为一种消息允许进行多种处理这里采用执行所有策略请自行在各策略中判断是否需要执行
for (ReceiveMsgStrategy value : ReceiveMsgFactory.getAllStrategyContainers().values()) {
value.doHandle(dto);
}
} else {
// 单独调用某一种
// 这里自己把消息类型转为自己的枚举类型
String handleType = "1";
ReceiveMsgStrategy receiveMsgStrategy = ReceiveMsgFactory.getStrategy(handleType);
receiveMsgStrategy.doHandle(dto);
}
} }
} }
log.debug("[收到消息]-[消息内容]-打印:{}", dto); log.debug("[收到消息]-[消息内容]-打印:{}", dto);
} }
private void receiveMsgForward(String jsonString) { private void receiveMsgCallback(String jsonString) {
// 开启转发且转发地址不为空 // 开启回调且回调地址不为空
if (weChatFerryProperties.getReceiveMsgFwdSwitch() && !CollectionUtils.isEmpty(weChatFerryProperties.getReceiveMsgFwdUrls())) { if (weChatFerryProperties.getReceiveMsgCallbackSwitch() && !CollectionUtils.isEmpty(weChatFerryProperties.getReceiveMsgCallbackUrls())) {
for (String receiveMsgFwdUrl : weChatFerryProperties.getReceiveMsgFwdUrls()) { for (String receiveMsgFwdUrl : weChatFerryProperties.getReceiveMsgCallbackUrls()) {
if (!receiveMsgFwdUrl.startsWith("http")) { if (!receiveMsgFwdUrl.startsWith("http")) {
continue; continue;
} }
try { try {
String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString); String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString);
if (judgeSuccess(responseStr)) { if (judgeSuccess(responseStr)) {
log.error("[接收消息]-消息转发外部接口,获取响应状态失败!-URL{}", receiveMsgFwdUrl); log.error("[接收消息]-消息回调至外部接口,获取响应状态失败!-URL{}", receiveMsgFwdUrl);
} }
log.debug("[接收消息]-[转发接收到的消息]-转发消息至:{}", receiveMsgFwdUrl); log.debug("[接收消息]-[回调接收到的消息]-回调消息至:{}", receiveMsgFwdUrl);
} catch (Exception e) { } catch (Exception e) {
log.error("[接收消息]-消息转发接口[{}]服务异常:", receiveMsgFwdUrl, e); log.error("[接收消息]-消息回调接口[{}]服务异常:", receiveMsgFwdUrl, e);
} }
} }
} }

View File

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

View File

@ -0,0 +1,84 @@
package com.wechat.ferry.strategy.msg.receive;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import com.wechat.ferry.enums.ReceiveMsgChannelEnum;
import com.wechat.ferry.exception.BizException;
import lombok.extern.slf4j.Slf4j;
/**
* 策略Context-消息处理-接收消息
* 可以切换策略的Context这里实际是Factory
*
* @author chandler
* @date 2024-12-25 14:08
*/
@Slf4j
@Component
public class ReceiveMsgFactory implements InitializingBean {
private static final Map<String, ReceiveMsgStrategy> strategyContainerMap = new HashMap<>();
/**
* spring的上下文
*/
@Resource
private ApplicationContext applicationContext;
/**
* 实现InitializingBean的方法会在启动的时候执行afterPropertiesSet()方法
* 将Strategy的类都按照定义好的规则fetchKey放入Map中
*/
@Override
public void afterPropertiesSet() {
// 初始化时把所有的策略bean放进ioc,用于使用的时候获取
Map<String, ReceiveMsgStrategy> strategyMap = applicationContext.getBeansOfType(ReceiveMsgStrategy.class);
strategyMap.forEach((k, v) -> {
String type = v.getStrategyType();
log.debug("[策略Context]-[MessageNoticeSendFactory]-策略类型加载:{}", type);
strategyContainerMap.putIfAbsent(type, v);
});
}
/**
* 根据策略类型获取不同处理策略类
*
* @param strategyType 策略类型
* @return 策略类
*/
public static ReceiveMsgStrategy getStrategy(String strategyType) {
log.debug("[策略Context]-[ReceiveMsgStrategy]-当前策略类型:{}", strategyType);
// 策略类对应的枚举
if (!ReceiveMsgChannelEnum.codeMap.containsKey(strategyType)) {
// 当前的渠道类型未匹配到
log.error("入参中的策略类型:{}不在枚举(ReceiveMsgChannelEnum)定义范围内,请检查数据合法性!", strategyType);
// TODO 正常所有的策略都应该在枚举中定义但考虑到有些人是把项目集成到自己系统中部分自己的策略类未在枚举中定义所以这里不抛异常但是我们建议开启
// throw new BizException("当前策略未在枚举中定义,请先在枚举中指定");
}
ReceiveMsgStrategy handler = strategyContainerMap.get(strategyType);
if (handler == null) {
log.error("[策略Context]-[MessageNoticeSendFactory]-策略类型:{}-未找到合适的处理器!", strategyType);
throw new BizException("未找到合适的处理器!");
}
return handler;
}
/**
* 获取全部策略
* 用于需要全部执行的情况
*
* @return 所有的策略类
*/
public static Map<String, ReceiveMsgStrategy> getAllStrategyContainers() {
return strategyContainerMap;
}
}

View File

@ -0,0 +1,27 @@
package com.wechat.ferry.strategy.msg.receive;
import com.wechat.ferry.entity.dto.WxPpMsgDTO;
/**
* 策略接口-消息处理-接收消息处理
*
* @author chandler
* @date 2024-12-25 14:07
*/
public interface ReceiveMsgStrategy {
/**
* 获取策略的类型
*
* @return 返回代表策略类型的字符串
*/
String getStrategyType();
/**
* 具体的处理
*
* @return 如果是多字段可以转为JSON字符串返回已适配不同的返回数据
*/
String doHandle(WxPpMsgDTO dto);
}

View File

@ -0,0 +1,33 @@
package com.wechat.ferry.strategy.msg.receive.impl;
import com.wechat.ferry.entity.dto.WxPpMsgDTO;
import com.wechat.ferry.enums.ReceiveMsgChannelEnum;
import com.wechat.ferry.strategy.msg.receive.ReceiveMsgStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 策略实现类-接收消息-签到处理
*
* @author chandler
* @date 2024-12-25 14:19
*/
@Slf4j
@Component
public class SignInMsgStrategyImpl implements ReceiveMsgStrategy {
@Override
public String getStrategyType() {
log.debug("[接收消息]-[签到处理]-匹配到:{}-{}-策略", ReceiveMsgChannelEnum.SIGN_IN.getCode(), ReceiveMsgChannelEnum.SIGN_IN.getName());
return ReceiveMsgChannelEnum.SIGN_IN.getCode();
}
@Override
public String doHandle(WxPpMsgDTO dto) {
// TODO 这里写具体的操作
// 当前是使用的所有策略类全部执行 所以这里需要控制哪种类型才处理
log.info("签到处理");
return "";
}
}

View File

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

View File

@ -0,0 +1,193 @@
package com.wechat.ferry.task;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.wechat.ferry.aggregation.facade.ContactDo;
import com.wechat.ferry.config.WeChatFerryProperties;
import com.wechat.ferry.enums.WxContactsTypeEnum;
import com.wechat.ferry.handle.WeChatSocketClient;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class ContactGroupMonitorTask {
private WeChatSocketClient wechatSocketClient;
@Autowired
public void setWechatSocketClient(WeChatSocketClient wechatSocketClient) {
this.wechatSocketClient = wechatSocketClient;
}
@Resource
private WeChatFerryProperties weChatFerryProperties;
/**
* 全部联系人信息集合 key:微信标识-weChatUid val:联系人信息
*/
private final Map<String, ContactDo> allContactDoMap = new HashMap<>();
/**
* 个微联系人信息集合 key:微信标识-weChatUid val:联系人信息
*/
private final Map<String, String> ppContactDoMap = new HashMap<>();
/**
* 企微联系人信息集合 key:微信标识-weChatUid val:联系人信息
*/
private final Map<String, String> cpContactDoMap = new HashMap<>();
/**
* 群组信息集合 key:群ID val:群名称
*/
private final Map<String, String> groupMap = new HashMap<>();
/**
* 群成员信息集合 key:群ID val:微信标识-weChatUid
*/
private final Map<String, Map<String, String>> groupMemberMap = new HashMap<>();
/**
* 初始化状态
*/
private Boolean initStatus = false;
@Scheduled(cron = "0 0 0 * * ?")
public void scheduled() {
if (true) {
// 目前查询的所有的联系人未判断群组是否已退出等状态故该功能暂不启用仅提供一个小样例
return;
}
ContactDo contactDo = new ContactDo();
// 查询联系人
List<ContactDo> contactList = contactDo.queryContactListBySql(wechatSocketClient, weChatFerryProperties);
log.info("[定时任务]-[联系人群组监控]-{}", contactList.size());
// 调用联系人监控处理
contactMonitor(contactList);
log.info("[定时任务]-[联系人群组监控]-结束");
}
// 联系人监控
private void contactMonitor(List<ContactDo> contactList) {
// 新增个微联系人
List<String> addPpContactList = new ArrayList<>();
// 删除个微联系人
List<String> deletePpContactList = new ArrayList<>();
// 新增企微联系人
List<String> addCpContactList = new ArrayList<>();
// 删除企微联系人
List<String> deleteCpContactList = new ArrayList<>();
// 新增群组
List<String> addGroupList = new ArrayList<>();
// 退出群组
List<String> deleteGroupList = new ArrayList<>();
// 本次个微联系人标识列表
List<String> nowPpContactIdList = new ArrayList<>();
// 本次企微联系人标识列表
List<String> nowCpContactIdList = new ArrayList<>();
// 本次群组标识列表
List<String> nowGroupIdList = new ArrayList<>();
// 开始匹配
if (!CollectionUtils.isEmpty(contactList)) {
// 判断全部联系人是否为空
if (allContactDoMap.isEmpty()) {
for (ContactDo contactDo : contactList) {
// 首次初始化
allContactDoMap.put(contactDo.getWeChatUid(), contactDo);
if (WxContactsTypeEnum.PERSON.getCode().equals(contactDo.getContactType())) {
// 个微-初始化
ppContactDoMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
} else if (WxContactsTypeEnum.WORK.getCode().equals(contactDo.getContactType())) {
// 企业微信-初始化
cpContactDoMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
} else if (WxContactsTypeEnum.GROUP.getCode().equals(contactDo.getContactType())) {
// 群组-初始化
groupMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
}
}
log.info("[定时任务]-[联系人监控]-首次初始化成功");
} else {
// 检测新增联系人
for (ContactDo contactDo : contactList) {
if (WxContactsTypeEnum.PERSON.getCode().equals(contactDo.getContactType())) {
// 个微
nowPpContactIdList.add(contactDo.getWeChatUid());
if (!ppContactDoMap.containsKey(contactDo.getWeChatUid())) {
// 个微-新增
addPpContactList.add(contactDo.getWeChatUid());
ppContactDoMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
}
} else if (WxContactsTypeEnum.WORK.getCode().equals(contactDo.getContactType())) {
// 企业微信
nowCpContactIdList.add(contactDo.getWeChatUid());
if (!cpContactDoMap.containsKey(contactDo.getWeChatUid())) {
// 企业微信-新增
addCpContactList.add(contactDo.getWeChatUid());
cpContactDoMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
}
} else if (WxContactsTypeEnum.GROUP.getCode().equals(contactDo.getContactType())) {
// 群组
nowGroupIdList.add(contactDo.getWeChatUid());
if (!groupMap.containsKey(contactDo.getWeChatUid())) {
// 群组-新增
addGroupList.add(contactDo.getWeChatUid());
groupMap.put(contactDo.getWeChatUid(), contactDo.getNickname());
}
}
}
}
// 初始化完成
if (initStatus) {
// 个微
for (Map.Entry<String, String> entry : ppContactDoMap.entrySet()) {
if (!nowPpContactIdList.contains(entry.getKey())) {
// 个微-删除
deletePpContactList.add(entry.getKey());
}
}
// 企微
for (Map.Entry<String, String> entry : cpContactDoMap.entrySet()) {
if (!nowCpContactIdList.contains(entry.getKey())) {
// 企微-删除
deleteCpContactList.add(entry.getKey());
}
}
// 群组
for (Map.Entry<String, String> entry : groupMap.entrySet()) {
if (!nowGroupIdList.contains(entry.getKey())) {
// 群组-删除
deleteGroupList.add(entry.getKey());
log.info("\"{}\"离开了群聊");
}
}
log.info("[定时任务]-[联系人监控]-个微新增:{},个微删除:{},企微新增:{},企微删除:{},群组新增:{},群组删除:{}", addPpContactList, deletePpContactList, addCpContactList,
deleteCpContactList, addGroupList, deleteGroupList);
}
}
initStatus = true;
}
// 监控群成员
private void groupMemberMonitor() {
if (!groupMap.isEmpty()) {
List<String> groupIdList = new ArrayList<>(groupMap.keySet());
for (String groupId : groupIdList) {
// 查询
}
}
}
}

View File

@ -35,15 +35,15 @@ wechat:
# 需要开启消息处理的群 # 需要开启消息处理的群
open-msg-groups: open-msg-groups:
- 53257911728@chatroom - 53257911728@chatroom
# 接收消息转发开关 # 接收消息回调开关
receive-msg-fwd-switch: false receive-msg-callback-switch: false
# 接收消息转发URL # 接收消息回调地址
receive-msg-fwd-urls: receive-msg-callback-urls:
- http://localhost:9001/msg - http://localhost:9001/msg
# 发送消息转发标识 1-关闭 2-全转发 3-发送成功才转发 # 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调
send-msg-fwd-flag: '1' send-msg-callback-flag: '1'
# 发送消息转发URL # 发送消息回调地址
send-msg-fwd-urls: send-msg-callback-urls:
- http://localhost:9001/msg - http://localhost:9001/msg
# 调用第三方服务客户端成功状态码 # 调用第三方服务客户端成功状态码
third-party-ok-codes: third-party-ok-codes: