From b3b57c13d1705400b71cc75564053b47ba222a64 Mon Sep 17 00:00:00 2001 From: mofung1 <2279254178@qq.com> Date: Tue, 21 Mar 2023 14:51:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E9=AA=8C=E8=AF=81=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../channel/ChannelOaCallBackController.java | 45 ++++ .../service/IChannelOaCallBackService.java | 29 +++ .../impl/ChannelOaCallBackServiceImpl.java | 197 ++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 server/like-admin/src/main/java/com/mdd/admin/controller/channel/ChannelOaCallBackController.java create mode 100644 server/like-admin/src/main/java/com/mdd/admin/service/IChannelOaCallBackService.java create mode 100644 server/like-admin/src/main/java/com/mdd/admin/service/impl/ChannelOaCallBackServiceImpl.java diff --git a/server/like-admin/src/main/java/com/mdd/admin/controller/channel/ChannelOaCallBackController.java b/server/like-admin/src/main/java/com/mdd/admin/controller/channel/ChannelOaCallBackController.java new file mode 100644 index 00000000..a7d91bf6 --- /dev/null +++ b/server/like-admin/src/main/java/com/mdd/admin/controller/channel/ChannelOaCallBackController.java @@ -0,0 +1,45 @@ +package com.mdd.admin.controller.channel; + + +import com.mdd.admin.service.IChannelOaCallBackService; +import io.swagger.annotations.Api; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + + +@AllArgsConstructor +@RestController +@RequestMapping("api/channel/oa") +@Api(tags = "公众号服务器验证及消息回复") +public class ChannelOaCallBackController { + + + @Resource + private IChannelOaCallBackService iChannelOaCallBackService; + + // 公众号服务器验证, 消息回复 + @GetMapping(name = "/callback", produces = "text/plain;charset=utf-8") + public String authGet(@RequestParam(name = "signature", required = false) String signature, + @RequestParam(name = "timestamp", required = false) String timestamp, + @RequestParam(name = "nonce", required = false) String nonce, + @RequestParam(name = "echostr", required = false) String echostr) { + return iChannelOaCallBackService.checkSignature(signature, timestamp, nonce, echostr); + } + + + // 消息回复 + @PostMapping(produces = "application/xml; charset=UTF-8") + public String post(@RequestBody String requestBody, + @RequestParam("signature") String signature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam(name = "encrypt_type", required = false) String encType, + @RequestParam(name = "msg_signature", required = false) String msgSignature) { + + return iChannelOaCallBackService.post(requestBody, signature, timestamp, nonce, encType, msgSignature); + } + + +} diff --git a/server/like-admin/src/main/java/com/mdd/admin/service/IChannelOaCallBackService.java b/server/like-admin/src/main/java/com/mdd/admin/service/IChannelOaCallBackService.java new file mode 100644 index 00000000..2a42bab9 --- /dev/null +++ b/server/like-admin/src/main/java/com/mdd/admin/service/IChannelOaCallBackService.java @@ -0,0 +1,29 @@ +package com.mdd.admin.service; + + +/** + * 公众号服务器回调 + */ +public interface IChannelOaCallBackService { + + + /** + * 服务器验证 + */ + String checkSignature(String signature, String timestamp, String nonce, String echostr); + + + /** + * 消息回复 + * + * @param requestBody + * @param signature + * @param timestamp + * @param nonce + * @param encType + * @param msgSignature + * @return + */ + String post(String requestBody, String signature, String timestamp, String nonce, String encType, String msgSignature); + +} diff --git a/server/like-admin/src/main/java/com/mdd/admin/service/impl/ChannelOaCallBackServiceImpl.java b/server/like-admin/src/main/java/com/mdd/admin/service/impl/ChannelOaCallBackServiceImpl.java new file mode 100644 index 00000000..c7199d45 --- /dev/null +++ b/server/like-admin/src/main/java/com/mdd/admin/service/impl/ChannelOaCallBackServiceImpl.java @@ -0,0 +1,197 @@ +package com.mdd.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.mdd.admin.service.IChannelOaCallBackService; +import com.mdd.common.entity.OfficialReply; +import com.mdd.common.exception.OperateException; +import com.mdd.common.mapper.OfficialReplyMapper; +import com.mdd.common.plugin.wechat.WxMnpDriver; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; +import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +import java.util.List; + +import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE; + +@Service +public class ChannelOaCallBackServiceImpl implements IChannelOaCallBackService { + + + @Resource + private OfficialReplyMapper officialReplyMapper; + + + /** + * 服务器验证 + * + * @param signature + * @param timestamp + * @param nonce + * @param echostr + * @return + */ + @Override + public String checkSignature(String signature, String timestamp, String nonce, String echostr) { + WxMpService wxMpService = WxMnpDriver.oa(); + if (wxMpService.checkSignature(timestamp, nonce, signature)) { + // 消息合法, 原样返回echostr + return echostr; + } + return "非法请求"; + } + + + /** + * 消息回复 + * + * @param requestBody + * @param signature + * @param timestamp + * @param nonce + * @param encType + * @param msgSignature + * @return + */ + @Override + public String post(String requestBody, String signature, String timestamp, String nonce, String encType, String msgSignature) { + + WxMpService wxMpService = WxMnpDriver.oa(); + if (!wxMpService.checkSignature(timestamp, nonce, signature)) { + throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); + } + + String out = null; + if (encType == null) { + // 明文传输的消息 + WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); + WxMpXmlOutMessage outMessage = this.msgHandler(inMessage); + if (outMessage == null) { + return ""; + } + out = outMessage.toXml(); + + } else if ("aes".equalsIgnoreCase(encType)) { + // aes加密的消息 + WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxMpService.getWxMpConfigStorage(), + timestamp, nonce, msgSignature); + + WxMpXmlOutMessage outMessage = this.msgHandler(inMessage); + if (outMessage == null) { + return ""; + } + out = outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage()); + } + + return out; + } + + /** + * 消息处理 + * + * @param wxMessage + * @return + */ + private WxMpXmlOutMessage msgHandler(WxMpXmlMessage wxMessage) { + try { + // 文本消息 + if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { + String msg = keyMsg(wxMessage); + return textBuild(msg, wxMessage); + } + + // 事件消息 + if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) { + if (wxMessage.getEvent().equals(SUBSCRIBE)) { + // 关注回复 + String msg = subMsg(); + return textBuild(msg, wxMessage); + } + } + } catch (Exception e) { + throw new OperateException("公众号回调错误" + e.getMessage()); + } + return null; + } + + /** + * 返回文本消息 + * + * @param content + * @param wxMessage + * @return + */ + private WxMpXmlOutMessage textBuild(String content, WxMpXmlMessage wxMessage) { + return WxMpXmlOutMessage.TEXT().content(content) + .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) + .build(); + } + + /** + * 关键词回复 + * + * @param wxMessage + * @return + */ + private String keyMsg(WxMpXmlMessage wxMessage) { + List oaReplyList = officialReplyMapper.selectList( + new QueryWrapper() + .eq("reply_type", 2) + .eq("is_delete", 0) + .eq("status", 1) + .orderByAsc("id")); + + String msg = null; + for (OfficialReply oaReply : oaReplyList) { + // 全匹配 + if (oaReply.getMatchingType() == 1 && oaReply.getKeyword().equals(wxMessage.getContent())) { + msg = oaReply.getContent(); + } + // 模糊匹配 + if (oaReply.getMatchingType() == 2 && wxMessage.getContent().contains(oaReply.getKeyword())) { + msg = oaReply.getContent(); + } + } + + return msg == null ? defaultMsg() : msg; + } + + /** + * 默认回复 + * + * @return + */ + private String defaultMsg() { + OfficialReply officialReply = officialReplyMapper.selectOne(new QueryWrapper() + .eq("reply_type", 3) + .eq("is_delete", 0) + .eq("status", 1) + .last("limit 1")); + if (officialReply == null) { + return null; + } + return officialReply.getContent(); + } + + /** + * 关注回复内容 + * + * @return + */ + private String subMsg() { + OfficialReply officialReply = officialReplyMapper.selectOne(new QueryWrapper() + .eq("reply_type", 1) + .eq("is_delete", 0) + .eq("status", 1) + .last("limit 1")); + if (officialReply == null) { + return defaultMsg(); + } + return officialReply.getContent(); + } + +}