diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java new file mode 100644 index 00000000..60ed4ff4 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/IChatVxService.java @@ -0,0 +1,19 @@ +package org.ruoyi.service; + +/** + * 企业微信聊天管理Service接口 + * + * @author ageerle + * @date 2025-04-08 + */ +public interface IChatVxService { + + + /** + * 企业微信应用回复 + * @param prompt 提示词 + * @return 回复内容 + */ + String chat(String prompt); + +} diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java new file mode 100644 index 00000000..7b013b53 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/service/impl/ChatVxServiceImpl.java @@ -0,0 +1,37 @@ +package org.ruoyi.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ruoyi.common.chat.entity.chat.ChatCompletion; +import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse; +import org.ruoyi.common.chat.entity.chat.Message; +import org.ruoyi.common.chat.openai.OpenAiStreamClient; +import org.ruoyi.service.IChatVxService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ChatVxServiceImpl implements IChatVxService { + + private final OpenAiStreamClient openAiStreamClient; + + @Override + public String chat(String prompt) { + List messageList = new ArrayList<>(); + Message message = Message.builder().role(Message.Role.USER).content(prompt).build(); + messageList.add(message); + ChatCompletion chatCompletion = ChatCompletion + .builder() + .messages(messageList) + .model("gpt-4o-mini") + .stream(false) + .build(); + ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion); + return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString(); + } + +} diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml index 1b3ff0c4..044e0bc9 100644 --- a/ruoyi-modules/pom.xml +++ b/ruoyi-modules/pom.xml @@ -21,6 +21,7 @@ ruoyi-chat ruoyi-system ruoyi-generator + ruoyi-wechat diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java index c0256427..1fb66bab 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/ISseService.java @@ -48,11 +48,4 @@ public interface ISseService { UploadFileResponse upload(MultipartFile file); - /** - * 企业应用回复 - * @param prompt 提示词 - * @return 回复内容 - */ - String wxCpChat(String prompt); - } diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java index eb0ae4ed..b6759f57 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java @@ -14,8 +14,6 @@ import org.ruoyi.chat.util.IpUtil; import org.ruoyi.chat.util.SSEUtil; import org.ruoyi.common.chat.config.LocalCache; import org.ruoyi.common.chat.entity.Tts.TextToSpeech; -import org.ruoyi.common.chat.entity.chat.ChatCompletion; -import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse; import org.ruoyi.common.chat.entity.chat.Message; import org.ruoyi.common.chat.entity.files.UploadFileResponse; import org.ruoyi.common.chat.entity.whisper.WhisperResponse; @@ -271,20 +269,4 @@ public class SseServiceImpl implements ISseService { return file; } - - @Override - public String wxCpChat(String prompt) { - List messageList = new ArrayList<>(); - Message message = Message.builder().role(Message.Role.USER).content(prompt).build(); - messageList.add(message); - ChatCompletion chatCompletion = ChatCompletion - .builder() - .messages(messageList) - .model("gpt-4o-mini") - .stream(false) - .build(); - ChatCompletionResponse chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion); - return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString(); - } - } diff --git a/ruoyi-modules/ruoyi-wechat/pom.xml b/ruoyi-modules/ruoyi-wechat/pom.xml new file mode 100644 index 00000000..8a716654 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + org.ruoyi + ruoyi-modules + ${revision} + ../pom.xml + + + ruoyi-wechat + + + 17 + 17 + UTF-8 + + + + + org.ruoyi + ruoyi-chat-api + + + + diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java new file mode 100644 index 00000000..6e97f5d1 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/AbstractBuilder.java @@ -0,0 +1,12 @@ +package org.ruoyi.builder; + +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; + +/** + * @author Binary Wang + */ +public abstract class AbstractBuilder { + public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service); +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java new file mode 100644 index 00000000..f6786b01 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/ImageBuilder.java @@ -0,0 +1,24 @@ +package org.ruoyi.builder; + +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutImageMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; + +/** + * @author Binary Wang + */ +public class ImageBuilder extends AbstractBuilder { + + @Override + public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, + WxCpService service) { + + WxCpXmlOutImageMessage m = WxCpXmlOutMessage.IMAGE().mediaId(content) + .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) + .build(); + + return m; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java new file mode 100644 index 00000000..707471e8 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/builder/TextBuilder.java @@ -0,0 +1,22 @@ +package org.ruoyi.builder; + +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutTextMessage; + +/** + * @author Binary Wang + */ +public class TextBuilder extends AbstractBuilder { + + @Override + public WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, + WxCpService service) { + WxCpXmlOutTextMessage m = WxCpXmlOutMessage.TEXT().content(content) + .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) + .build(); + return m; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java new file mode 100644 index 00000000..8a5f0fba --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpConfiguration.java @@ -0,0 +1,130 @@ +package org.ruoyi.config; + + +import com.google.common.collect.Maps; +import jakarta.annotation.PostConstruct; +import lombok.val; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.api.impl.WxCpServiceImpl; +import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl; +import me.chanjar.weixin.cp.constant.WxCpConsts; +import me.chanjar.weixin.cp.message.WxCpMessageHandler; +import me.chanjar.weixin.cp.message.WxCpMessageRouter; +import org.ruoyi.handler.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 单实例配置 + * + * @author Binary Wang + */ +@Configuration +@EnableConfigurationProperties(WxCpProperties.class) +public class WxCpConfiguration { + private final LogHandler logHandler; + private NullHandler nullHandler; + private LocationHandler locationHandler; + private MenuHandler menuHandler; + private MsgHandler msgHandler; + private final UnsubscribeHandler unsubscribeHandler; + private SubscribeHandler subscribeHandler; + private WxCpProperties properties; + + private static Map routers = Maps.newHashMap(); + private static Map cpServices = Maps.newHashMap(); + + @Autowired + public WxCpConfiguration(LogHandler logHandler, NullHandler nullHandler, LocationHandler locationHandler, + MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler, + SubscribeHandler subscribeHandler, WxCpProperties properties) { + this.logHandler = logHandler; + this.nullHandler = nullHandler; + this.locationHandler = locationHandler; + this.menuHandler = menuHandler; + this.msgHandler = msgHandler; + this.unsubscribeHandler = unsubscribeHandler; + this.subscribeHandler = subscribeHandler; + this.properties = properties; + } + + + public static Map getRouters() { + return routers; + } + + public static WxCpService getCpService(Integer agentId) { + return cpServices.get(agentId); + } + + @PostConstruct + public void initServices() { + cpServices = this.properties.getAppConfigs().stream().map(a -> { + val configStorage = new WxCpDefaultConfigImpl(); + configStorage.setCorpId(this.properties.getCorpId()); + configStorage.setAgentId(a.getAgentId()); + configStorage.setCorpSecret(a.getSecret()); + configStorage.setToken(a.getToken()); + configStorage.setAesKey(a.getAesKey()); + val service = new WxCpServiceImpl(); + service.setWxCpConfigStorage(configStorage); + routers.put(a.getAgentId(), this.newRouter(service)); + return service; + }).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a)); + } + + private WxCpMessageRouter newRouter(WxCpService wxCpService) { + final val newRouter = new WxCpMessageRouter(wxCpService); + + // 记录所有事件的日志 (异步执行) + newRouter.rule().handler(this.logHandler).next(); + + // 自定义菜单事件 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end(); + + // 点击菜单链接事件(这里使用了一个空的处理器,可以根据自己需要进行扩展) + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end(); + + // 关注事件 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler) + .end(); + + // 取消关注事件 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.EventType.UNSUBSCRIBE) + .handler((WxCpMessageHandler) this.unsubscribeHandler).end(); + + // 上报地理位置事件 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.EventType.LOCATION).handler(this.locationHandler) + .end(); + + // 接收地理位置消息 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) + .handler(this.locationHandler).end(); + + // 扫码事件(这里使用了一个空的处理器,可以根据自己需要进行扩展) + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxConsts.EventType.SCAN).handler((WxCpMessageHandler) this.nullHandler).end(); + + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end(); + + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) + .event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end(); + + // 默认 + newRouter.rule().async(false).handler(this.msgHandler).end(); + + return newRouter; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java new file mode 100644 index 00000000..51077d61 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/config/WxCpProperties.java @@ -0,0 +1,45 @@ +package org.ruoyi.config; + +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +/** + * @author Binary Wang + */ +@Data +public class WxCpProperties { + /** + * 设置企业微信的corpId + */ + private String corpId; + + private List appConfigs; + + @Getter + @Setter + public static class AppConfig { + /** + * 设置企业微信应用的AgentId + */ + private Integer agentId; + + /** + * 设置企业微信应用的Secret + */ + private String secret; + + /** + * 设置企业微信应用的token + */ + private String token; + + /** + * 设置企业微信应用的EncodingAESKey + */ + private String aesKey; + + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java new file mode 100644 index 00000000..2857bc2f --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/controller/wxcplogin/WxPortalController.java @@ -0,0 +1,86 @@ +package org.ruoyi.controller.wxcplogin; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil; +import org.apache.commons.lang3.StringUtils; +import org.ruoyi.common.core.utils.JsonUtils; + +import org.ruoyi.config.WxCpConfiguration; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +/** + * @author Binary Wang + */ +@RestController +@RequestMapping("/wx/cp") +@Slf4j +public class WxPortalController { + + @Value("${wechat.cp.appConfigs[0].agentId}") + private Integer agentId; + + + @GetMapping(produces = "text/plain;charset=utf-8") + public String authGet( + @RequestParam(name = "msg_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) { + log.info("\n接收到来自微信服务器的认证消息:signature = [{}], timestamp = [{}], nonce = [{}], echostr = [{}]", + signature, timestamp, nonce, echostr); + + if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { + throw new IllegalArgumentException("请求参数非法,请核实!"); + } + + final WxCpService wxCpService = WxCpConfiguration.getCpService(agentId); + if (wxCpService == null) { + throw new IllegalArgumentException(String.format("未找到对应agentId=[%d]的配置,请核实!", agentId)); + } + + if (wxCpService.checkSignature(signature, timestamp, nonce, echostr)) { + return new WxCpCryptUtil(wxCpService.getWxCpConfigStorage()).decrypt(echostr); + } + + return "非法请求"; + } + + @PostMapping(produces = "application/xml; charset=UTF-8") + public String post( + @RequestBody String requestBody, + @RequestParam("msg_signature") String signature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce) { + log.info("\n接收微信请求:[signature=[{}], timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", + signature, timestamp, nonce, requestBody); + + final WxCpService wxCpService = WxCpConfiguration.getCpService(1000002); + WxCpXmlMessage inMessage = WxCpXmlMessage.fromEncryptedXml(requestBody, wxCpService.getWxCpConfigStorage(), + timestamp, nonce, signature); + log.debug("\n消息解密后内容为:\n{} ", JsonUtils.toJson(inMessage)); + WxCpXmlOutMessage outMessage = this.route(1000002, inMessage); + if (outMessage == null) { + return ""; + } + + String out = outMessage.toEncryptedXml(wxCpService.getWxCpConfigStorage()); + log.debug("\n组装回复信息:{}", out); + return out; + } + + private WxCpXmlOutMessage route(Integer agentId, WxCpXmlMessage message) { + try { + return WxCpConfiguration.getRouters().get(agentId).route(message); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java new file mode 100644 index 00000000..7ec2b0ce --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/AbstractHandler.java @@ -0,0 +1,9 @@ +package org.ruoyi.handler; + +import me.chanjar.weixin.cp.message.WxCpMessageHandler; + +/** + * @author Binary Wang + */ +public abstract class AbstractHandler implements WxCpMessageHandler { +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java new file mode 100644 index 00000000..178229db --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ContactChangeHandler.java @@ -0,0 +1,34 @@ +package org.ruoyi.handler; + + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.ruoyi.builder.TextBuilder; +import org.ruoyi.common.core.utils.JsonUtils; + +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 通讯录变更事件处理器. + * + * @author Binary Wang + */ +@Slf4j +@Component +public class ContactChangeHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + String content = "收到通讯录变更事件,内容:" + JsonUtils.toJson(wxMessage); + log.info(content); + + return new TextBuilder().build(content, wxMessage, cpService); + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java new file mode 100644 index 00000000..c98fe68a --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/EnterAgentHandler.java @@ -0,0 +1,29 @@ +package org.ruoyi.handler; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; + +import java.util.Map; + +/** + *
+ *
+ * Created by Binary Wang on 2018/8/27.
+ * 
+ * + * @author Binary Wang + */ +@Slf4j +public class EnterAgentHandler extends AbstractHandler { + private static final int TEST_AGENT = 1000002; + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException { + // do something + return null; + } +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java new file mode 100644 index 00000000..56d1fb34 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LocationHandler.java @@ -0,0 +1,44 @@ +package org.ruoyi.handler; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.ruoyi.builder.TextBuilder; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Slf4j +@Component +public class LocationHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.LOCATION)) { + //TODO 接收处理用户发送的地理位置消息 + try { + String content = "感谢反馈,您的的地理位置已收到!"; + return new TextBuilder().build(content, wxMessage, null); + } catch (Exception e) { + log.error("位置消息接收处理失败", e); + return null; + } + } + + //上报地理位置事件 + log.info("\n上报地理位置,纬度 : {}\n经度 : {}\n精度 : {}", + wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision())); + + //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 + + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java new file mode 100644 index 00000000..ea12c40d --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/LogHandler.java @@ -0,0 +1,26 @@ +package org.ruoyi.handler; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.ruoyi.common.core.utils.JsonUtils; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Slf4j +@Component +public class LogHandler extends AbstractHandler { + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + log.info("\n接收到请求消息,内容:{}", JsonUtils.toJson(wxMessage)); + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java new file mode 100644 index 00000000..803db7fa --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MenuHandler.java @@ -0,0 +1,34 @@ +package org.ruoyi.handler; + +import me.chanjar.weixin.common.api.WxConsts.MenuButtonType; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Component +public class MenuHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + + String msg = String.format("type:%s, event:%s, key:%s", + wxMessage.getMsgType(), wxMessage.getEvent(), + wxMessage.getEventKey()); + if (MenuButtonType.VIEW.equals(wxMessage.getEvent())) { + return null; + } + + return WxCpXmlOutMessage.TEXT().content(msg) + .fromUser(wxMessage.getToUserName()).toUser(wxMessage.getFromUserName()) + .build(); + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java new file mode 100644 index 00000000..6527371e --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/MsgHandler.java @@ -0,0 +1,43 @@ +package org.ruoyi.handler; + +import lombok.RequiredArgsConstructor; +import me.chanjar.weixin.common.api.WxConsts; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; + +import org.ruoyi.builder.TextBuilder; +import org.ruoyi.service.IChatVxService; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Component +@RequiredArgsConstructor +public class MsgHandler extends AbstractHandler { + + private final IChatVxService chatVxService; + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + final String msgType = wxMessage.getMsgType(); + if (msgType == null) { + // 如果msgType没有,就自己根据具体报文内容做处理 + } + + if (!msgType.equals(WxConsts.XmlMsgType.EVENT)) { + //TODO 可以选择将消息保存到本地 + } + //TODO 组装回复消息 + String content = chatVxService.chat(wxMessage.getContent()); + + return new TextBuilder().build(content, wxMessage, cpService); + + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java new file mode 100644 index 00000000..2677d73d --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/NullHandler.java @@ -0,0 +1,23 @@ +package org.ruoyi.handler; + +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Component +public class NullHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java new file mode 100644 index 00000000..4d9a5025 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/ScanHandler.java @@ -0,0 +1,8 @@ +package org.ruoyi.handler; + +/** + * @author Binary Wang + */ +public abstract class ScanHandler extends AbstractHandler { + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java new file mode 100644 index 00000000..c5e42fb4 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/SubscribeHandler.java @@ -0,0 +1,63 @@ +package org.ruoyi.handler; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.WxCpUser; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.ruoyi.builder.TextBuilder; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Slf4j +@Component +public class SubscribeHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) throws WxErrorException { + + log.info("新关注用户 OPENID: " + wxMessage.getFromUserName()); + + // 获取微信用户基本信息 + WxCpUser userWxInfo = cpService.getUserService().getById(wxMessage.getFromUserName()); + + if (userWxInfo != null) { + // TODO 可以添加关注用户到本地 + } + + WxCpXmlOutMessage responseResult = null; + try { + responseResult = handleSpecial(wxMessage); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + if (responseResult != null) { + return responseResult; + } + + try { + return new TextBuilder().build("感谢关注", wxMessage, cpService); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + + /** + * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 + */ + private WxCpXmlOutMessage handleSpecial(WxCpXmlMessage wxMessage) { + //TODO + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java new file mode 100644 index 00000000..77933460 --- /dev/null +++ b/ruoyi-modules/ruoyi-wechat/src/main/java/org/ruoyi/handler/UnsubscribeHandler.java @@ -0,0 +1,28 @@ +package org.ruoyi.handler; + +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.session.WxSessionManager; +import me.chanjar.weixin.cp.api.WxCpService; +import me.chanjar.weixin.cp.bean.message.WxCpXmlMessage; +import me.chanjar.weixin.cp.bean.message.WxCpXmlOutMessage; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author Binary Wang + */ +@Slf4j +@Component +public class UnsubscribeHandler extends AbstractHandler { + + @Override + public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map context, WxCpService cpService, + WxSessionManager sessionManager) { + String openId = wxMessage.getFromUserName(); + log.info("取消关注用户 OPENID: " + openId); + // TODO 可以更新本地数据库为取消关注状态 + return null; + } + +}