diff --git a/README.MD b/README.MD index 25aa7487..4a205feb 100644 --- a/README.MD +++ b/README.MD @@ -54,6 +54,7 @@ * Python * HTTP * NodeJS +* Java @@ -79,6 +80,7 @@ pip install --upgrade wcferry * [wcf-http](https://github.com/yuxiaoli/wcf-http)(基于 Python) ### Java +* [SpringBoot版](https://github.com/PathfinderAx/WeChatFerry)(基于SpringBoot+Maven:`为避免代码冲突,请先至此仓库提交PR后统一合并至主仓库`) * [java](clients/java/wcferry/README.MD) ### NodeJS diff --git a/clients/java/wcf-bmc/CHANGELOG.md b/clients/java/wcf-bmc/CHANGELOG.md index 8bbc5a95..04117167 100644 --- a/clients/java/wcf-bmc/CHANGELOG.md +++ b/clients/java/wcf-bmc/CHANGELOG.md @@ -63,6 +63,13 @@ ___ v39.5.2版本目前会出现注入失败的情况,待排查修复,可先使用v39.5.1,只须替换dll版本即可 +### 2025-05-19 + +- 1.配置文件位置变更,方便大家自行配置 +- 2.日志优化 + +
+ ### 2025-05-04 - 1.更新拍一拍接口入参 diff --git a/clients/java/wcf-bmc/README.MD b/clients/java/wcf-bmc/README.MD index 2fa111dd..9c94b8f9 100644 --- a/clients/java/wcf-bmc/README.MD +++ b/clients/java/wcf-bmc/README.MD @@ -5,6 +5,9 @@ `声明:` 本项目是基于 clients/java/wcferry 项目改造,随着时间推进,项目结构和代码规范逐渐产生分离,使用此项目的人员可参考之前的项目 我们在开发时请尽量保持注释的完整性,便于阅读维护 +* [仓库地址:https://github.com/PathfinderAx/WeChatFerry](https://github.com/PathfinderAx/WeChatFerry)(基于SpringBoot+Maven:`为避免代码冲突,请先至此仓库提交PR后统一合并至主仓库`) + + ## 快速使用 ### 环境准备 @@ -47,7 +50,9 @@ ### 修改配置文件 -配置文件:src/main/resources/application.yml +配置文件: +- src/main/resources/application.yml +- src/main/resources/config/application-dev.yml 根据自己的dll目录位置修改配置文件 @@ -122,6 +127,7 @@ wcf-bmc │ ├─config 配置文件目录 │ ├─application.yml 配置文件 +│ ├─application-dev.yml 开发环境配置文件 │ └─logback-spring.xml 日志配置文件 │ ├─wcf-bmc-xx.x.x.x.jar 打包后的源码包 @@ -261,6 +267,10 @@ message RoomData ``` +### 开发约定 + +* 1.控制层仅处理数据操作,异常请在业务层进行抛出,系统会自动捕获并返回给前端 + ### 提交规范 本模块希望大家使用统一提交格式,便于区分 diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/config/WeChatFerryProperties.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/config/WeChatFerryProperties.java index 1d33770b..2dcb4d05 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/config/WeChatFerryProperties.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/config/WeChatFerryProperties.java @@ -34,6 +34,11 @@ public class WeChatFerryProperties { */ private Boolean sdkDebugSwitch = false; + /** + * 文件下载保存位置 + */ + private String fileSavePath; + /** * 联系人类型-官方杂号,禁止与其他分类重复(格式:代码|名称) * 使用时记得需要提取代码或者名称匹配 diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/controller/WeChatDllController.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/controller/WeChatDllController.java index 488d6765..8aeadb5c 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/controller/WeChatDllController.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/controller/WeChatDllController.java @@ -1,6 +1,5 @@ package com.wechat.ferry.controller; -import com.alibaba.fastjson2.JSONObject; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +14,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq; -import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq; +import com.wechat.ferry.entity.vo.request.WxPpWcfFileSaveReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq; @@ -43,12 +42,10 @@ import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp; import com.wechat.ferry.enums.ResponseCodeEnum; import com.wechat.ferry.service.WeChatDllService; -import com.wechat.ferry.utils.PathUtils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StringUtils; /** * 控制层-微信DLL处理 @@ -263,67 +260,36 @@ public TResponse receiveTransfer(@Validated @RequestBody WxPpWcfReceiveT } /** - * 下载视频 add by wmz 2025-05-01 + * 文件保存至本机 * - * @param request - * @return - * @throws Exception - */ - @ApiOperation(value = "下载视频", notes = "download_video") - @PostMapping(value = "/download/video") - public TResponse downloadVideo(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception { - String path = weChatDllService.downloadVideo(request); - if (path != null) { - JSONObject pathJson = new JSONObject(); - pathJson.put("path", path); - return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson); - } - return TResponse.ok(ResponseCodeEnum.FAILED); - } - - /** - * 下载图片 add by wmz 2025-05-02 + * @param request 请求入参 + * @return 保存到本机的路径 * - * @param request - * @return - * @throws Exception + * @author zm + * @date 2025-05-01 */ - @ApiOperation(value = "下载图片", notes = "download_picture") - @PostMapping(value = "/download/picture") - public TResponse downloadPicture(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception { - //check parameter - String dir = request.getDir(); - if (!StringUtils.hasText(dir)) { - log.info("需要指定图片的路径dir"); - return TResponse.fail("需要指定图片的路径dir"); - } - boolean res = PathUtils.createDir(dir); - if (!res) { - return TResponse.fail("图片路径创建失败" + dir); - } - - String path = weChatDllService.downloadPicture(request); - if (path != null) { - JSONObject pathJson = new JSONObject(); - pathJson.put("path", path); - return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson); - } - return TResponse.ok(ResponseCodeEnum.FAILED); + @ApiOperation(value = "文件保存至本机", notes = "fileSaveToLocal") + @PostMapping(value = "/file/saveToLocal") + public TResponse fileSaveToLocal(@Validated @RequestBody WxPpWcfFileSaveReq request) { + String path = weChatDllService.fileSaveToLocal(request); + return TResponse.ok(ResponseCodeEnum.SUCCESS, path); } /** - * 暂未实现 add by mz 2025-05-01 + * 暂未实现 TODO * - * @param request + * @param request 请求入参 * @return - * @throws Exception + * + * + * @author zm + * @date 2025-05-01 */ - @ApiOperation(value = "登陆二维码", notes = "loginQR") - @PostMapping(value = "/loginQR") - public TResponse loginQR(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception { - String path = weChatDllService.loginQR(); + @ApiOperation(value = "登陆二维码", notes = "loginQrCode") + @PostMapping(value = "/loginQrCode") + public TResponse loginQrCode(@Validated @RequestBody WxPpWcfFileSaveReq request) { + String path = weChatDllService.loginQrCode(); return TResponse.ok(ResponseCodeEnum.SUCCESS, path); } - } diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDownloadAttachReq.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDownloadAttachReq.java deleted file mode 100644 index be2fc177..00000000 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfDownloadAttachReq.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.wechat.ferry.entity.vo.request; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.Data; - -import javax.validation.constraints.NotBlank; - -/** - * 请求入参-下载附件信息 - * - * @author wmz - * @date 2025-05-02 - */ -@Data -@ApiModel(value = "wxPpWcfDownloadAttachReq", description = "个微WCF下载附件请求入参") -public class WxPpWcfDownloadAttachReq { - - /** - * 消息接收人 - * 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx) - * 群聊为 roomid(xxxxxxxxxx@chatroom) - */ - @NotBlank(message = "消息id不能为空") - @ApiModelProperty(value = "消息id") - private Long id; - - /** - * 文件的extra - */ - @ApiModelProperty(value = "extra") - private String extra; - - /** - * 缩略图thumb - */ -// @NotBlank(message = "thumb不能为空") - @ApiModelProperty(value = "缩略图thumb") - private String thumb; - - /** - * dir (str): 存放图片的目录。下载图片需要。暂不支持视频 - */ - @ApiModelProperty(value = "图片存放路径dir") - private String dir; - -} diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfFileSaveReq.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfFileSaveReq.java new file mode 100644 index 00000000..d5deddb0 --- /dev/null +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfFileSaveReq.java @@ -0,0 +1,57 @@ +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 wmz + * @date 2025-05-02 + */ +@Data +@ApiModel(value = "wxPpWcfFileSaveReq", description = "个微WCF文件保存请求入参") +public class WxPpWcfFileSaveReq { + + /** + * 消息编号 + */ + @NotBlank(message = "消息编号不能为空") + @ApiModelProperty(value = "消息编号") + private Long msgId; + + /** + * 消息中的extra + */ + @ApiModelProperty(value = "extra") + private String extra; + + /** + * 缩略图的链接 + */ + @ApiModelProperty(value = "缩略图的链接") + private String thumbnailUrl; + + /** + * 文件保存路径 + */ + @ApiModelProperty(value = "文件保存路径") + private String savePath; + + /** + * 文件类型后缀 + * 如:.png + */ + @ApiModelProperty(value = "文件类型后缀") + private String fileType; + + /** + * 超时时间(秒) + */ + @ApiModelProperty(value = "超时时间(秒)") + private Integer timeout = 30; + +} diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfRevokeMsgReq.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfRevokeMsgReq.java index fe84b094..abb30b2e 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfRevokeMsgReq.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/entity/vo/request/WxPpWcfRevokeMsgReq.java @@ -20,6 +20,6 @@ public class WxPpWcfRevokeMsgReq { * 消息编号 */ @ApiModelProperty(value = "消息编号") - private String msgId; + private Long msgId; } diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/handle/WeChatSocketClient.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/handle/WeChatSocketClient.java index 558b24f7..160cdd0c 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/handle/WeChatSocketClient.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/handle/WeChatSocketClient.java @@ -131,7 +131,7 @@ public Response sendCmd(Request req) { // modify by wmz 2025-05-03 cmdSocket.setSendTimeout(5000); cmdSocket.setReceiveTimeout(5000); - + ByteBuffer bb = ByteBuffer.wrap(req.toByteArray()); cmdSocket.send(bb); ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE); @@ -180,13 +180,14 @@ private void listenMsg(String url) { * * @return 是否登录结果 */ - public Response getQrcode() { + public int getQrcode() { Request req = Request.newBuilder().setFuncValue(Functions.FUNC_REFRESH_QRCODE_VALUE).build(); Response rsp = sendCmd(req); + int ret = -1; if (rsp != null) { - return rsp; + ret = rsp.getStatus(); } - return null; + return ret; } /** @@ -601,7 +602,7 @@ public int refreshPyq(Integer id) { * @param extra 消息中的 extra * @return 结果状态码 0 为成功,其他失败 */ - public int downloadAttach(Integer id, String thumb, String extra) { + public int downloadAttach(Long id, String thumb, String extra) { int ret = -1; Wcf.AttachMsg attachMsg = Wcf.AttachMsg.newBuilder().setId(id).setThumb(thumb).setExtra(extra).build(); Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DOWNLOAD_ATTACH_VALUE).setAtt(attachMsg).build(); @@ -638,7 +639,7 @@ public Wcf.RpcContact getInfoByWxId(String wxid) { * @param id 待撤回消息的 id * @return 结果状态码 0 为成功,其他失败 */ - public int revokeMsg(Integer id) { + public int revokeMsg(Long id) { int ret = -1; Request req = Request.newBuilder().setFuncValue(Functions.FUNC_REVOKE_MSG_VALUE).setUi64(id).build(); Response rsp = sendCmd(req); @@ -659,12 +660,10 @@ public int revokeMsg(Integer id) { * @param src 加密的图片路径 * @param dir 保存图片的目录 * @return 解密图片的保存路径 - * @see WeChatDllServiceImpl decryptImage */ - @Deprecated public String decryptImage(String src, String dir) { - Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(src).setDst(dir).build(); - Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build(); + Wcf.DecPath decPath = Wcf.DecPath.newBuilder().setSrc(src).setDst(dir).build(); + Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(decPath).build(); Response rsp = sendCmd(req); if (rsp != null) { return rsp.getStr(); diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/WeChatDllService.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/WeChatDllService.java index 9e80826b..381671fb 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/WeChatDllService.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/WeChatDllService.java @@ -6,7 +6,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq; -import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq; +import com.wechat.ferry.entity.vo.request.WxPpWcfFileSaveReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq; @@ -337,7 +337,7 @@ WxPpWcfSendRichTextMsgResp sendRichTextMsg(String recipient, String name, String * @author chandler * @date 2025-05-04 13:40 */ - String revokeMsg(String msgId); + String revokeMsg(Long msgId); /** * 通过好友申请 @@ -489,38 +489,40 @@ WxPpWcfSendRichTextMsgResp sendRichTextMsg(String recipient, String name, String String receiveTransfer(String weChatUid, String transferId, String transactionId); /** - * 下载视频文件 + * 文件保存至本机 * * @param request 请求入参 - * @return 文件路径 + * @return 保存到本机的路径 * * @author wmz - * @throws java.lang.Exception * @date 2025-05-02 */ - String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception; - + String fileSaveToLocal(WxPpWcfFileSaveReq request); + /** - * 下载图片 + * 文件保存至本机 * - * @param request 请求入参 - * @return 文件路径 + * @param msgId 消息编号 + * @param extra 消息中的extra + * @param thumbnailUrl 缩略图的链接 + * @param savePath 文件保存路径 + * @param fileType 文件类型后缀,如:.png + * @param timeout 超时时间(秒) + * @return 保存到本机的路径 * * @author wmz - * @throws java.lang.Exception * @date 2025-05-02 */ - String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception; - + String fileSaveToLocal(Long msgId, String extra, String thumbnailUrl, String savePath, String fileType, Integer timeout); + /** * 获取登录二维码 * * @return 文件路径 * * @author wmz - * @throws java.lang.Exception * @date 2025-05-02 */ - String loginQR() throws Exception; + String loginQrCode(); } diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatDllServiceImpl.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatDllServiceImpl.java index e3c8ab1c..b9769475 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatDllServiceImpl.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatDllServiceImpl.java @@ -1,9 +1,11 @@ package com.wechat.ferry.service.impl; +import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; @@ -11,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; @@ -22,7 +25,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq; import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq; -import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq; +import com.wechat.ferry.entity.vo.request.WxPpWcfFileSaveReq; import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq; import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq; @@ -57,8 +60,7 @@ import com.wechat.ferry.handle.WeChatSocketClient; import com.wechat.ferry.service.WeChatDllService; import com.wechat.ferry.utils.HttpClientUtil; -import com.wechat.ferry.utils.PathUtils; -import java.io.File; +import com.wechat.ferry.utils.PathUtil; import lombok.extern.slf4j.Slf4j; @@ -434,7 +436,7 @@ public String revokeMsg(WxPpWcfRevokeMsgReq request) { // 公共校验 checkClientStatus(); log.info("[撤回消息]-[消息撤回]-入参打印:{}", request); - Integer msgId = Integer.valueOf(request.getMsgId()); + Long msgId = request.getMsgId(); // FUNC_REVOKE_MSG_VALUE int state = wechatSocketClient.revokeMsg(msgId); // 回调处理 @@ -444,7 +446,7 @@ public String revokeMsg(WxPpWcfRevokeMsgReq request) { } @Override - public String revokeMsg(String msgId) { + public String revokeMsg(Long msgId) { WxPpWcfRevokeMsgReq request = new WxPpWcfRevokeMsgReq(); request.setMsgId(msgId); return this.revokeMsg(request); @@ -646,6 +648,79 @@ public String receiveTransfer(String weChatUid, String transferId, String transa return this.receiveTransfer(request); } + @Override + public String fileSaveToLocal(WxPpWcfFileSaveReq request) { + long startTime = System.currentTimeMillis(); + // 公共校验 + checkClientStatus(); + log.info("[文件保存]-[保存至本机]-入参打印:{}", request); + if (ObjectUtils.isEmpty(request.getSavePath())) { + request.setSavePath(weChatFerryProperties.getFileSavePath()); + log.info("[文件保存]-[保存至本机]-入参中的保存路径为空,重置文件保存路径:{}", request.getSavePath()); + } + // 校验文件路径 + checkFileResourcePath(request.getSavePath()); + // 下载入库 + downloadDbAttach(request.getMsgId(), request.getThumbnailUrl(), request.getExtra()); + String filePath = ""; + // 判断是否是图片 + if (!ObjectUtils.isEmpty(request.getExtra()) && request.getExtra().contains("/Image/")) { + // 解密图片-下载图片 + // FUNC_DECRYPT_IMAGE_VALUE + filePath = wechatSocketClient.decryptImage(request.getExtra(), request.getSavePath()); + // 强制等待2秒 + forceSleep(2000); + } else { + // 非图片 + String base = PathUtil.removeExtension(request.getSavePath()); + filePath = base + request.getFileType(); + } + String localPath = ""; + for (int i = 0; i < request.getTimeout(); i++) { + if (new File(filePath).exists()) { + localPath = filePath; + log.info("[文件保存]-[保存至本机]-保存完毕:{}", localPath); + break; + } else { + log.info("等待保存至本机,保存中......{}", i); + forceSleep(1000); + } + } + long endTime = System.currentTimeMillis(); + log.info("[文件保存]-[保存至本机]-处理结束,耗时:{}ms", (endTime - startTime)); + return localPath; + } + + @Override + public String fileSaveToLocal(Long msgId, String extra, String thumbnailUrl, String savePath, String fileType, Integer timeout) { + if (ObjectUtils.isEmpty(timeout)) { + timeout = 30; + } + WxPpWcfFileSaveReq request = new WxPpWcfFileSaveReq(); + request.setMsgId(msgId); + request.setExtra(extra); + request.setThumbnailUrl(thumbnailUrl); + request.setSavePath(savePath); + request.setFileType(fileType); + request.setTimeout(timeout); + return this.fileSaveToLocal(request); + } + + @Override + public String loginQrCode() { + // 公共校验 + checkClientStatus(); + try { + // # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 + Thread.sleep(1000); + // 第一步 + int state = wechatSocketClient.getQrcode(); + } catch (Exception e) { + throw new BizException("获取二维码失败"); + } + return ""; + } + /** * 消息回调 * @@ -765,131 +840,57 @@ private void checkClientStatus() { throw new BizException("微信客户端未登录或状态异常,请人工关闭本服务之后,退出微信客户端在重启本服务!"); } } - - - @Override - public String loginQR() throws Exception { - - long startTime = System.currentTimeMillis(); - // 公共校验 - checkClientStatus(); - log.info("[登录]-[获取二维码]-入参打印:{}", ""); -// # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 - Thread.sleep(1000); - //第一步 - Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_REFRESH_QRCODE_VALUE) - .build(); - Wcf.Response rsp = wechatSocketClient.sendCmd(req); - rsp.getStatus(); -// RequestResponseBodyMethodProcessor s; - long endTime = System.currentTimeMillis(); - log.info("[登录]-[获取二维码]-处理结束,耗时:{}ms", (endTime - startTime)); - System.out.println(rsp.getStr()); - return rsp.getStr(); - } - - @Override - public String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception { - long startTime = System.currentTimeMillis(); - // 公共校验 - checkClientStatus(); - log.info("[下载]-[下载视频]-入参打印:{}", request); - int status = attachDownload(request.getId(), "", request.getThumb()); - if (status != 0) { - log.info("{}:下载视频出错", request.getId()); - return null; - } - //第二步,检测文件,指定下载的目录 - String base = PathUtils.removeExtension(request.getThumb()); - String filePath = base + ".mp4"; - String path = null; - for (int i = 0; i < 30; i++) { - if (new File(filePath).exists()) { - path = filePath; - log.info("视频下载完毕:{}", path); - break; - } else { - log.info("等待下载中:{}", i); - Thread.sleep(1000); - } - } - long endTime = System.currentTimeMillis(); - log.info("[下载]-[下载视频]-处理结束,耗时:{}ms", (endTime - startTime)); - return path; - } - @Override - public String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception { - long startTime = System.currentTimeMillis(); - // 公共校验 - checkClientStatus(); - log.info("[下载]-[下载图片]-入参打印:{}", request); - int status = attachDownload(request.getId(), request.getExtra(), ""); - if (status != 0) { - log.info("{}:下载出错", request.getId()); - return null; + /** + * 校验文件路径 + * + * @author wmz + * @date 2025-05-02 + */ + private void checkFileResourcePath(String fileDirectory) { + if (!StringUtils.hasText(fileDirectory)) { + log.error("需要指定图片的路径dir"); + throw new BizException("需要指定图片的路径dir"); } - //第二步解密图片--下载图片 - String filePath = decryptImage(request.getExtra(), request.getDir()); - String path = null; - for (int i = 0; i < 15; i++) { - if (new File(filePath).exists()) { - log.info("图片下载完毕:{}", filePath); - path = filePath; - break; - } else { - log.info("等待下载中:{}", i); - Thread.sleep(1000); - } + boolean res = PathUtil.createDir(fileDirectory); + if (!res) { + throw new BizException("图片路径创建失败:{}", fileDirectory); } - long endTime = System.currentTimeMillis(); - log.info("[下载]-[下载图片]-处理结束,耗时:{}ms", (endTime - startTime)); - return path; } /** - * 下载附件(图片、视频、文件)。这方法别直接调用。 + * 等待文件入库 * - * @param id - * @param extra - * @param thumb - * @return int: 0 为成功, 其他失败。 - * @throws Exception + * @author chandler + * @date 2025-05-02 */ - private int attachDownload(long id, String extra, String thumb) { - try { - //# 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时 - Thread.sleep(1000); - //第一步,下载 - Wcf.AttachMsg msg = Wcf.AttachMsg.newBuilder().setId(id) - .setExtra(extra).setThumb(thumb) - .build(); - Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DOWNLOAD_ATTACH_VALUE) - .setAtt(msg) - .build(); - Wcf.Response rsp = wechatSocketClient.sendCmd(req); - return rsp.getStatus(); - } catch (Exception e) { - e.printStackTrace(); + private void downloadDbAttach(Long msgId, String thumbnailUrl, String extra) { + // 强制等待2秒 + forceSleep(2000); + // FUNC_DOWNLOAD_ATTACH_VALUE + int state = wechatSocketClient.downloadAttach(msgId, thumbnailUrl, extra); + if (state != 0) { + log.error("{}:下载视频出错", msgId); + throw new BizException("下载视频出错"); } - return -1; + // 强制等待2秒 + forceSleep(2000); } /** - * 解密图片 + * 强制休眠 * - * @param srcPath 加密的图片路径 - * @param dir 保存图片的目录 - * @return 是否成功 + * @author chandler + * @date 2025-05-02 */ - public String decryptImage(String srcPath, String dir) { - Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(srcPath).setDst(dir).build(); - Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build(); - Wcf.Response rsp = wechatSocketClient.sendCmd(req); - if (rsp != null && rsp.getStatus() == 0) { - return rsp.getStr(); + private void forceSleep(long milliseconds) { + try { + // # 强制等待 + TimeUnit.MILLISECONDS.sleep(milliseconds); + } catch (InterruptedException e) { + // 处理中断异常 + e.printStackTrace(); } - return null; } } diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatMsgServiceImpl.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatMsgServiceImpl.java index 7ea128a4..bd4bbed0 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatMsgServiceImpl.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatMsgServiceImpl.java @@ -43,6 +43,7 @@ public void receiveMsg(String jsonString) { receiveMsgCallback(jsonString); // 转为JSON对象 WxPpMsgDTO dto = JSON.parseObject(jsonString, WxPpMsgDTO.class); + log.debug("[接收消息]-[消息内容]-打印:{}", dto); // 有开启的群聊配置 if (weChatFerryProperties.getOpenMsgGroupSwitch() && !weChatFerryProperties.getOpenMsgGroups().isEmpty()) { Map> openMsgGroupMap = new LinkedHashMap<>(); @@ -79,7 +80,7 @@ public void receiveMsg(String jsonString) { // 指定处理的群聊 if (!openMsgGroupMap.isEmpty()) { - log.debug("[收到消息后处理]-[汇总后的所有功能]-openMsgGroupMap:{}", openMsgGroupMap); + log.debug("[接收消息]-[功能处理]-[汇总后的所有功能]-openMsgGroupMap:{}", openMsgGroupMap); List fnNoList = new ArrayList<>(); // 先执行所有群都需要执行的 if (openMsgGroupMap.containsKey("ALL")) { @@ -91,16 +92,18 @@ public void receiveMsg(String jsonString) { } // 需要执行的策略 if (!CollectionUtils.isEmpty(fnNoList)) { - log.debug("[收到消息后处理]-[汇总后的单群功能]-fnNoList:{},群号:{}", fnNoList, dto.getRoomId()); + log.debug("[接收消息]-[功能处理]-fnNoList:{},群号:{}", fnNoList, dto.getRoomId()); for (String no : fnNoList) { // 根据功能号获取对应的策略 ReceiveMsgStrategy receiveMsgStrategy = ReceiveMsgFactory.getStrategy(no); - receiveMsgStrategy.doHandle(dto); + if (!ObjectUtils.isEmpty(receiveMsgStrategy)) { + receiveMsgStrategy.doHandle(dto); + } } } } } - log.debug("[收到消息]-[消息内容]-打印:{}", dto); + log.debug("[接收消息]-msgId:[{}],本条消息处理完毕!", dto.getId()); } private void receiveMsgCallback(String jsonString) { @@ -113,11 +116,11 @@ private void receiveMsgCallback(String jsonString) { try { String responseStr = HttpClientUtil.doPostJson(receiveMsgFwdUrl, jsonString); if (judgeSuccess(responseStr)) { - log.error("[接收消息]-消息回调至外部接口,获取响应状态失败!-URL:{}", receiveMsgFwdUrl); + log.error("[接收消息]-[消息回调]-消息回调至外部接口,获取响应状态失败!-URL:{}", receiveMsgFwdUrl); } - log.debug("[接收消息]-[回调接收到的消息]-回调消息至:{}", receiveMsgFwdUrl); + log.debug("[接收消息]-[消息回调]-[回调接收到的消息]-回调消息至:{}", receiveMsgFwdUrl); } catch (Exception e) { - log.error("[接收消息]-消息回调接口[{}]服务异常:", receiveMsgFwdUrl, e); + log.error("[接收消息]-[消息回调]-消息回调接口[{}]服务异常:", receiveMsgFwdUrl, e); } } } diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgFactory.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgFactory.java index 39fb5b06..e5706a79 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgFactory.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/strategy/msg/receive/ReceiveMsgFactory.java @@ -43,7 +43,7 @@ public void afterPropertiesSet() { Map strategyMap = applicationContext.getBeansOfType(ReceiveMsgStrategy.class); strategyMap.forEach((k, v) -> { String type = v.getStrategyType(); - log.debug("[策略Context]-[MessageNoticeSendFactory]-策略类型加载:{}", type); + log.debug("[策略Context]-[MessageNoticeSendFactory]-初始化策略加载:{}", type); strategyContainerMap.putIfAbsent(type, v); }); } @@ -55,7 +55,7 @@ public void afterPropertiesSet() { * @return 策略类 */ public static ReceiveMsgStrategy getStrategy(String strategyType) { - log.debug("[策略Context]-[ReceiveMsgStrategy]-当前策略类型:{}", strategyType); + log.debug("[策略Context]-[ReceiveMsgStrategy]-初始化当前策略类型:{}", strategyType); // 策略类对应的枚举 if (!ReceiveMsgChannelEnum.codeMap.containsKey(strategyType)) { // 当前的渠道类型未匹配到 diff --git a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtils.java b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtil.java similarity index 78% rename from clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtils.java rename to clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtil.java index 0b707a32..c10eccbd 100644 --- a/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtils.java +++ b/clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtil.java @@ -5,16 +5,21 @@ */ package com.wechat.ferry.utils; +import java.io.File; + /** + * 路径工具类 + * * @date 2025-05-02 * @author zm */ -import java.io.File; - -public class PathUtils { +public class PathUtil { /** * 分离文件路径,获取不带扩展名的部分 等同于 Python 的 os.path.splitext(thumb)[0] + * + * @date 2025-05-02 + * @author zm */ public static String removeExtension(String path) { int dotIndex = path.lastIndexOf('.'); @@ -26,6 +31,9 @@ public static String removeExtension(String path) { /** * 获取文件名(包含扩展名) 等同于 Python 的 os.path.basename(file_path) + * + * @date 2025-05-02 + * @author zm */ public static String getFileName(String path) { File file = new File(path); @@ -34,8 +42,12 @@ public static String getFileName(String path) { /** * 不存在则创建 - * @param dirPath - * @return + * + * @param dirPath + * @return + * + * @date 2025-05-02 + * @author zm */ public static boolean createDir(String dirPath) { File dir = new File(dirPath); @@ -48,7 +60,7 @@ public static boolean createDir(String dirPath) { return false; } } else { - //Nothing to do + // Nothing to do } return true; } @@ -57,9 +69,9 @@ public static boolean createDir(String dirPath) { public static void main(String[] args) { String thumb = "/tmp/video_cover.jpg"; - String base = removeExtension(thumb); // /tmp/video_cover - String filePath = base + ".mp4"; // /tmp/video_cover.mp4 - String fileName = getFileName(filePath); // video_cover.mp4 + String base = removeExtension(thumb); // /tmp/video_cover + String filePath = base + ".mp4"; // /tmp/video_cover.mp4 + String fileName = getFileName(filePath); // video_cover.mp4 System.out.println("base: " + base); System.out.println("filePath: " + filePath); diff --git a/clients/java/wcf-bmc/src/main/resources/application.yml b/clients/java/wcf-bmc/src/main/resources/application.yml index 38b83562..2e5934d4 100644 --- a/clients/java/wcf-bmc/src/main/resources/application.yml +++ b/clients/java/wcf-bmc/src/main/resources/application.yml @@ -9,7 +9,10 @@ spring: # 配置应用信息 application: # 应用名 - name: wcf-bmc + name: WCF-BMC + # 激活的文件 + profiles: + active: dev # swagger适配 mvc: pathmatch: @@ -17,43 +20,5 @@ spring: # 日志配置 logging: - config: classpath:logback-spring.xml - -# 本服务参数 -wechat: - ferry: - # DLL文件位置 - dll-path: E:\WeChatFerry\clients\java\wcf-bmc\dll\sdk.dll - # socket端口 - socket-port: 10086 - # SDK是否调试模式 - sdk-debug-switch: false - # 联系人类型-官方杂号,禁止与其他分类重复(格式:代码|名称) - contacts-type-mixed: - - filehelper|文件传输助手 - # 联系人类型-公众号,禁止与其他分类重复(格式:代码|名称) - contacts-type-official: - - weixinguanhaozhushou|微信公众平台 - # 接收消息回调开关 - open-msg-group-switch: false - # 需要开启消息处理的群 - open-msg-groups: - # key:"[群编号]" val:开启的功能号,对应ReceiveMsgChannelEnum枚举中的code - "[ALL]": '' - "[53257911730@chatroom]": '1,2,3' - "[50501762866@chatroom]": '1' - # 接收消息回调开关 - receive-msg-callback-switch: false - # 接收消息回调地址 - receive-msg-callback-urls: - - http://localhost:9001/msg - # 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调 - send-msg-callback-flag: '1' - # 发送消息回调地址 - send-msg-callback-urls: - - http://localhost:9001/msg - # 调用第三方服务客户端成功状态码 - third-party-ok-codes: - # key:状态码字段 val:状态码值 - code: '200' + config: classpath:config/logback-spring.xml diff --git a/clients/java/wcf-bmc/src/main/resources/config/application-dev.yml b/clients/java/wcf-bmc/src/main/resources/config/application-dev.yml new file mode 100644 index 00000000..dbb35306 --- /dev/null +++ b/clients/java/wcf-bmc/src/main/resources/config/application-dev.yml @@ -0,0 +1,42 @@ +# 配置文件 + +# 本服务参数 +wechat: + ferry: + # DLL文件位置 + dll-path: E:\WeChatFerry\clients\java\wcf-bmc\dll\sdk.dll + # socket端口 + socket-port: 10086 + # SDK是否调试模式 + sdk-debug-switch: false + # 文件下载保存位置 + file-save-path: E:\WeChatFerry\clients\java\wcf-bmc + # 联系人类型-官方杂号,禁止与其他分类重复(格式:代码|名称) + contacts-type-mixed: + - filehelper|文件传输助手 + # 联系人类型-公众号,禁止与其他分类重复(格式:代码|名称) + contacts-type-official: + - weixinguanhaozhushou|微信公众平台 + # 接收消息回调开关 + open-msg-group-switch: false + # 需要开启消息处理的群 + open-msg-groups: + # key:"[群编号]" val:开启的功能号,对应ReceiveMsgChannelEnum枚举中的code + "[ALL]": '' + "[53257911730@chatroom]": '1,2,3' + "[50501762866@chatroom]": '1' + # 接收消息回调开关 + receive-msg-callback-switch: false + # 接收消息回调地址 + receive-msg-callback-urls: + - http://localhost:9001/msg + # 发送消息回调标识 1-关闭 2-全部回调 3-发送成功才回调 + send-msg-callback-flag: '1' + # 发送消息回调地址 + send-msg-callback-urls: + - http://localhost:9001/msg + # 调用第三方服务客户端成功状态码 + third-party-ok-codes: + # key:状态码字段 val:状态码值 + code: '200' + diff --git a/clients/java/wcf-bmc/src/main/resources/logback-spring.xml b/clients/java/wcf-bmc/src/main/resources/config/logback-spring.xml similarity index 100% rename from clients/java/wcf-bmc/src/main/resources/logback-spring.xml rename to clients/java/wcf-bmc/src/main/resources/config/logback-spring.xml