mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-14 12:23:39 +00:00
feat: 新增wechat模块
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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<Message> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
<module>ruoyi-chat</module>
|
<module>ruoyi-chat</module>
|
||||||
<module>ruoyi-system</module>
|
<module>ruoyi-system</module>
|
||||||
<module>ruoyi-generator</module>
|
<module>ruoyi-generator</module>
|
||||||
|
<module>ruoyi-wechat</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -48,11 +48,4 @@ public interface ISseService {
|
|||||||
UploadFileResponse upload(MultipartFile file);
|
UploadFileResponse upload(MultipartFile file);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 企业应用回复
|
|
||||||
* @param prompt 提示词
|
|
||||||
* @return 回复内容
|
|
||||||
*/
|
|
||||||
String wxCpChat(String prompt);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import org.ruoyi.chat.util.IpUtil;
|
|||||||
import org.ruoyi.chat.util.SSEUtil;
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
import org.ruoyi.common.chat.config.LocalCache;
|
import org.ruoyi.common.chat.config.LocalCache;
|
||||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
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.chat.Message;
|
||||||
import org.ruoyi.common.chat.entity.files.UploadFileResponse;
|
import org.ruoyi.common.chat.entity.files.UploadFileResponse;
|
||||||
import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
|
import org.ruoyi.common.chat.entity.whisper.WhisperResponse;
|
||||||
@@ -271,20 +269,4 @@ public class SseServiceImpl implements ISseService {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String wxCpChat(String prompt) {
|
|
||||||
List<Message> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
28
ruoyi-modules/ruoyi-wechat/pom.xml
Normal file
28
ruoyi-modules/ruoyi-wechat/pom.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-modules</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>ruoyi-wechat</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-chat-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBuilder {
|
||||||
|
public abstract WxCpXmlOutMessage build(String content, WxCpXmlMessage wxMessage, WxCpService service);
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@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<Integer, WxCpMessageRouter> routers = Maps.newHashMap();
|
||||||
|
private static Map<Integer, WxCpService> 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<Integer, WxCpMessageRouter> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package org.ruoyi.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WxCpProperties {
|
||||||
|
/**
|
||||||
|
* 设置企业微信的corpId
|
||||||
|
*/
|
||||||
|
private String corpId;
|
||||||
|
|
||||||
|
private List<AppConfig> appConfigs;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public static class AppConfig {
|
||||||
|
/**
|
||||||
|
* 设置企业微信应用的AgentId
|
||||||
|
*/
|
||||||
|
private Integer agentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置企业微信应用的Secret
|
||||||
|
*/
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置企业微信应用的token
|
||||||
|
*/
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置企业微信应用的EncodingAESKey
|
||||||
|
*/
|
||||||
|
private String aesKey;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.ruoyi.handler;
|
||||||
|
|
||||||
|
import me.chanjar.weixin.cp.message.WxCpMessageHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
public abstract class AbstractHandler implements WxCpMessageHandler {
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ContactChangeHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
|
||||||
|
WxSessionManager sessionManager) {
|
||||||
|
String content = "收到通讯录变更事件,内容:" + JsonUtils.toJson(wxMessage);
|
||||||
|
log.info(content);
|
||||||
|
|
||||||
|
return new TextBuilder().build(content, wxMessage, cpService);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
*
|
||||||
|
* Created by Binary Wang on 2018/8/27.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class EnterAgentHandler extends AbstractHandler {
|
||||||
|
private static final int TEST_AGENT = 1000002;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService wxCpService, WxSessionManager sessionManager) throws WxErrorException {
|
||||||
|
// do something
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class LocationHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class LogHandler extends AbstractHandler {
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
|
||||||
|
WxSessionManager sessionManager) {
|
||||||
|
log.info("\n接收到请求消息,内容:{}", JsonUtils.toJson(wxMessage));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MenuHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MsgHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
private final IChatVxService chatVxService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class NullHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
|
||||||
|
WxSessionManager sessionManager) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.ruoyi.handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
public abstract class ScanHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SubscribeHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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 <a href="https://github.com/binarywang">Binary Wang</a>
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class UnsubscribeHandler extends AbstractHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WxCpXmlOutMessage handle(WxCpXmlMessage wxMessage, Map<String, Object> context, WxCpService cpService,
|
||||||
|
WxSessionManager sessionManager) {
|
||||||
|
String openId = wxMessage.getFromUserName();
|
||||||
|
log.info("取消关注用户 OPENID: " + openId);
|
||||||
|
// TODO 可以更新本地数据库为取消关注状态
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user