Browse Source

添加了公众号消息响应(未完成)

master
wei 6 years ago
parent
commit
1db14e671b
  1. 7
      pom.xml
  2. 234
      src/main/java/com/ccsens/ccmq/lowlevel/client/ClientManager.java
  3. 366
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/ChannelManager.java
  4. 200
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/WrapperedChannel.java
  5. 115
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusConverter.java
  6. 45
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusDecoder.java
  7. 33
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusEncoder.java
  8. 109
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusHandler.java
  9. 59
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/NettyMBServer.java
  10. 61
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/NettyTextServer.java
  11. 32
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextDecoder.java
  12. 49
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextEncoder.java
  13. 110
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextHandler.java
  14. 72
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/NettyWsServer.java
  15. 33
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketDecoder.java
  16. 25
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketEncoder.java
  17. 107
      src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketHandler.java
  18. 36
      src/main/java/com/ccsens/ccmq/lowlevel/client/rabbitmq/QueueManager.java
  19. 48
      src/main/java/com/ccsens/ccmq/lowlevel/client/rabbitmq/RabbitMqListener.java
  20. 28
      src/main/java/com/ccsens/ccmq/lowlevel/client/restful/MessageApi.java
  21. 16
      src/main/java/com/ccsens/ccmq/lowlevel/client/restful/RestManager.java
  22. 481
      src/main/java/com/ccsens/ccmq/lowlevel/message/MessageHandler.java
  23. 31
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/AckMessage.java
  24. 36
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/AuthMessage.java
  25. 16
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/ClientAuthTimeOutMessage.java
  26. 16
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/ClientIdleClosedMessage.java
  27. 16
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/GetStatusMessage.java
  28. 36
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/HasReadMessage.java
  29. 32
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/PingMessage.java
  30. 31
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetDeletedStatusMessage.java
  31. 31
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetRevertedStatusMessage.java
  32. 31
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetSuccessStatusMessage.java
  33. 31
      src/main/java/com/ccsens/ccmq/lowlevel/message/client/UnExceptedErrorMessage.java
  34. 95
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/InMessage.java
  35. 134
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/Message.java
  36. 217
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/MessageConstant.java
  37. 80
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/MessageRule.java
  38. 71
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/OutMessage.java
  39. 43
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/OutMessageSet.java
  40. 11
      src/main/java/com/ccsens/ccmq/lowlevel/message/common/ServerMessage.java
  41. 37
      src/main/java/com/ccsens/ccmq/lowlevel/message/server/ChannelStatusMessage.java
  42. 28
      src/main/java/com/ccsens/ccmq/lowlevel/message/server/InvertedMessage.java
  43. 15
      src/main/java/com/ccsens/ccmq/lowlevel/message/server/PongMessage.java
  44. 30
      src/main/java/com/ccsens/ccmq/lowlevel/message/server/QueueStatusMessage.java
  45. 49
      src/main/java/com/ccsens/ccmq/lowlevel/message/server/ServerAckMessage.java
  46. 118
      src/main/java/com/ccsens/ccmq/lowlevel/persist/IMessageDao.java
  47. 180
      src/main/java/com/ccsens/ccmq/lowlevel/persist/MessageDao.java
  48. 9
      src/main/java/com/ccsens/ccmq/lowlevel/service/IUserService.java
  49. 15
      src/main/java/com/ccsens/ccmq/lowlevel/service/UserService.java
  50. 36
      src/main/java/com/ccsens/opensource/wxconfigurer/WxConfigurerApplication.java
  51. 151
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/dto/WxGzhAction.java
  52. 15
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxAccessToken.java
  53. 96
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMenu.java
  54. 52
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMsgEvent.java
  55. 33
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMsgType.java
  56. 21
      src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxOperation.java
  57. 14
      src/main/java/com/ccsens/opensource/wxconfigurer/config/DruidProps.java
  58. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/config/EnjoyConfig.java
  59. 6
      src/main/java/com/ccsens/opensource/wxconfigurer/config/ExcepHandler.java
  60. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/config/RabbitMqConfig.java
  61. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/config/RedisConfig.java
  62. 4
      src/main/java/com/ccsens/opensource/wxconfigurer/config/RestClientConfig.java
  63. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/config/ServletConfig.java
  64. 21
      src/main/java/com/ccsens/opensource/wxconfigurer/config/SettingProps.java
  65. 4
      src/main/java/com/ccsens/opensource/wxconfigurer/config/SpringConfig.java
  66. 40
      src/main/java/com/ccsens/opensource/wxconfigurer/config/SwaggerConfig.java
  67. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/config/TaskExecutorConfig.java
  68. 21
      src/main/java/com/ccsens/opensource/wxconfigurer/exception/BaseException.java
  69. 18
      src/main/java/com/ccsens/opensource/wxconfigurer/exception/BusinessException.java
  70. 22
      src/main/java/com/ccsens/opensource/wxconfigurer/exception/WxException.java
  71. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/BeanWrapperUtil.java
  72. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/CRCUtil.java
  73. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/DateUtil.java
  74. 87
      src/main/java/com/ccsens/opensource/wxconfigurer/util/FileUtil.java
  75. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/GenericsUtils.java
  76. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpServletUtil.java
  77. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpsClientRequestFactory.java
  78. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpsUtil.java
  79. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/IDGenerator.java
  80. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/ImgUtil.java
  81. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/JacksonUtil.java
  82. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonPathUtil.java
  83. 62
      src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonResponse.java
  84. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonResponse1.java
  85. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/JwtUtil.java
  86. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/MyJacksonObjectMapper.java
  87. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/NotSupportedFileTypeException.java
  88. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/PasswordEncryptionUtil.java
  89. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/RedisKeyManager.java
  90. 3
      src/main/java/com/ccsens/opensource/wxconfigurer/util/RedisUtil.java
  91. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/ResourceLock.java
  92. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/SQLUtil.java
  93. 188
      src/main/java/com/ccsens/opensource/wxconfigurer/util/ServletUtil.java
  94. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/ShiroKit.java
  95. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/SpringContextUtils.java
  96. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/TokenBean.java
  97. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/TokenBean1.java
  98. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/ToolUtil.java
  99. 2
      src/main/java/com/ccsens/opensource/wxconfigurer/util/WebConstant.java
  100. 47
      src/main/java/com/ccsens/opensource/wxconfigurer/util/WechatUtil.java

7
pom.xml

@ -3,12 +3,12 @@
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>
<groupId>com.ccsens</groupId>
<artifactId>tall-message</artifactId>
<groupId>com.ccsens.opensource</groupId>
<artifactId>wxconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>tall-message</name>
<name>wxconfigure</name>
<description>Tall Message MicroService</description>
<parent>
@ -17,7 +17,6 @@
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

234
src/main/java/com/ccsens/ccmq/lowlevel/client/ClientManager.java

@ -1,234 +0,0 @@
package com.ccsens.ccmq.lowlevel.client;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.ccsens.ccmq.lowlevel.client.netty.WrapperedChannel;
import com.ccsens.ccmq.lowlevel.message.MessageHandler;
import com.ccsens.ccmq.lowlevel.message.client.ClientAuthTimeOutMessage;
import com.ccsens.ccmq.lowlevel.message.common.*;
import com.ccsens.ccmq.lowlevel.message.server.ServerAckMessage;
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager;
import com.ccsens.ccmq.lowlevel.client.rabbitmq.QueueManager;
import com.ccsens.ccmq.lowlevel.client.restful.RestManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import wiki.tall.ccmq.common.util.JacksonUtil;
import java.util.Iterator;
import java.util.Map;
import static com.ccsens.ccmq.lowlevel.message.MessageHandler.handleMessage;
/**
* @author __zHangSan
*/
@Component
public class ClientManager {
private static Logger logger = LoggerFactory.getLogger(ClientManager.class);
/**
* 未认证连接存活时间s
*/
private static final Long UnAuthedChannelMaxAliveTimeInSeconds = 30L;
@Scheduled(cron = "0/30 * * * * *")
public void closeUnAuthedChannels() throws Exception{
logger.debug("Invoke closeUnAuthedChannels({})",UnAuthedChannelMaxAliveTimeInSeconds);
Map<Channel, WrapperedChannel> allChannels = ChannelManager.getCopyOfAllChannels();
Iterator<Map.Entry<Channel,WrapperedChannel>> it = allChannels.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Channel,WrapperedChannel> entry = it.next();
WrapperedChannel wrapperedChannel = entry.getValue();
if((!wrapperedChannel.isAuthed()) && (wrapperedChannel.getConnectedSeconds() > UnAuthedChannelMaxAliveTimeInSeconds)) {
//构造AuthTimeOut消息并处理
try {
ChannelManager.setCurrentChannel(wrapperedChannel.getChannel());
MessageHandler.handleMessage(InMessage.newToServerMessage(MessageConstant.DomainType.User,
new ClientAuthTimeOutMessage()));
//close会触发remove
wrapperedChannel.getChannel().close();
logger.debug("Remove a unAuthed Channel {}, which has connected {}s,maxOutTime is {}s",
wrapperedChannel.getId(), wrapperedChannel.getConnectedSeconds(), UnAuthedChannelMaxAliveTimeInSeconds
);
}catch(Exception e){
e.printStackTrace();
logger.error("handle ClientAuthTimeOutMessage error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
}
}
}
@Scheduled(cron="*/30 * * * * ?")
public void showChannels(){
logger.debug("----------------- AuthedChannels(" + System.currentTimeMillis()/1000 + ")--------------");
ChannelManager.showAuthedChannels();
logger.debug("----------------- AllChannels -----------------");
ChannelManager.showAllChannels();
logger.debug("------------------End -------------------------");
}
public static InMessage fillMessageInfo(InMessage inMessage, MessageConstant.DomainType fromDomain){
//1. 填充from、toDomain、Rule
switch (fromDomain){
case User: {
//1.填充Message from信息
inMessage.setFromDomain(MessageConstant.DomainType.User);
inMessage.setFrom(ChannelManager.getUserIdByChannel(ChannelManager.getCurrentChannel()));
//2.填充Message to信息
if (null == inMessage.getToDomain()) {
inMessage.setToDomain(MessageConstant.DomainType.Queue);
}
//3.填充规则缺省值
if (null == inMessage.getRule()) {
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain()));
}
break;
}
case Queue: {
//1.填充Message from信息
inMessage.setFromDomain(MessageConstant.DomainType.Queue);
inMessage.setFrom(QueueManager.getInQueueName());
//2.填充Message to信息
if(null == inMessage.getToDomain()){
inMessage.setToDomain( MessageConstant.DomainType.User);
}
//3.填充规则缺省值
if (null == inMessage.getRule()) {
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain()));
}
break;
}
case Rest: {
//1.填充Message from信息
inMessage.setFromDomain(MessageConstant.DomainType.Rest);
inMessage.setFrom("");
//2.填充Message to信息
if (null == inMessage.getToDomain()) {
inMessage.setToDomain(MessageConstant.DomainType.User);
}
//3.填充规则缺省值
if (null == inMessage.getRule()) {
inMessage.setRule(MessageRule.defaultRule(inMessage.getFromDomain()));
}
break;
}
default:
break;
}
//2.填充tos值
if (null == inMessage.getTos()) {
inMessage.setTos(CollectionUtil.newHashSet());
} else {
inMessage.getTos().removeIf(to -> to == null || StrUtil.isEmpty(to));
}
switch (inMessage.getToDomain()) {
case Queue: {
if (CollectionUtil.isEmpty(inMessage.getTos())) {
inMessage.getTos().add(QueueManager.getOutQueueName());
}
break;
}
case Rest:{
if (CollectionUtil.isEmpty(inMessage.getTos())) {
inMessage.getTos().add(RestManager.getOutRestName());
}
break;
}
default: {
break;
}
}
return inMessage;
}
public static void sendMessageToAuthedClient(MessageConstant.DomainType toDomain, String to, OutMessageSet outMessageSet) throws JsonProcessingException {
switch (toDomain){
case User:
if(StrUtil.isNotEmpty(to)) {
ChannelManager.sendTo(to, outMessageSet);
}
break;
case Queue:
QueueManager.sendTo(outMessageSet);
break;
case Rest:
RestManager.sendTo(outMessageSet);
break;
default:
break;
}
}
public static void sendServerMessage(MessageConstant.DomainType toDomain, OutMessageSet outMessageSet) throws JsonProcessingException {
switch (toDomain){
case User:
ChannelManager.sendTo(ChannelManager.getCurrentChannel(), outMessageSet);
break;
case Queue:
QueueManager.sendTo(outMessageSet);
break;
case Rest:
RestManager.sendTo(outMessageSet);
break;
default:
break;
}
}
public static void sendServerAck(InMessage inMessage, MessageConstant.Error error) throws Exception {
switch (error) {
case Ok:
if (inMessage.getRule().getAckRule() != MessageRule.AckRule.ALWAYS) {
return;
}
break;
default:
if (inMessage.getRule().getAckRule() == MessageRule.AckRule.NONE) {
return;
}
break;
}
OutMessageSet outMessageSet = OutMessageSet.newInstance().ackId(null).add(
new OutMessage(MessageConstant.DomainType.Server,
JacksonUtil.beanToJson(
new ServerAckMessage(inMessage.getId(),inMessage.getUnikey(),error)
)
)
);
sendServerMessage(inMessage.getFromDomain(),outMessageSet);
}
public static boolean isSenderAuthed(InMessage inMessage) {
if (inMessage.getFromDomain() == MessageConstant.DomainType.User) {
return ChannelManager.channelAuthed(ChannelManager.getCurrentChannel());
}
return true;
}
public static void closeCurrentSender(InMessage message) {
if(message.getFromDomain() == MessageConstant.DomainType.User){
ChannelManager.getCurrentChannel().close();
}
}
public static boolean isUserOnline(MessageConstant.DomainType domain, String to) {
if (domain == MessageConstant.DomainType.User) {
return ChannelManager.isUserOnline(to);
}
return true;
}
}

366
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/ChannelManager.java

@ -1,366 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty;
import cn.hutool.core.collection.CollectionUtil;
import com.ccsens.ccmq.lowlevel.message.client.ClientAuthTimeOutMessage;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static com.ccsens.ccmq.lowlevel.message.MessageHandler.handleMessage;
/**
* @author wei
*/
public class ChannelManager {
private static Logger logger = LoggerFactory.getLogger(ChannelManager.class);
private static ThreadLocal<Channel> threadLocal = new ThreadLocal<>();
/**
* UserId,WrappedChannel authed channels;
*/
private static Map<String,Set<WrapperedChannel>> authedChannels;
/**
* Channel,WrapperedChannel
*/
private static Map<Channel,WrapperedChannel> rawChannels;
static {
authedChannels = new ConcurrentHashMap<>();
rawChannels = new ConcurrentHashMap<>();
}
/**
* 私有构造,不允许生成该类对象
*/
private ChannelManager(){}
/**
* 设置当前正在错误的channel
* @param channel netty ws/tcp连接
*/
public static void setCurrentChannel(Channel channel){
threadLocal.set(channel);
}
/**
* 获取当前线程的channel对象
* @return channel
*/
public static Channel getCurrentChannel(){
return threadLocal.get();
}
/**
* 删除当前线程的channel对象
*/
public static void removeCurrentChannel(){
threadLocal.remove();
}
/**
* 添加一个新的连接(Channel)
* @param channel 新的连接
* @param serverType 服务器类型
*/
public static synchronized void addChannel(Channel channel,String serverType){
logger.debug("Invoke addChannel({},{})",channel,serverType);
if(null != channel) {
rawChannels.put(channel, new WrapperedChannel(channel, serverType));
logger.debug("Add a new channel: {},{}",channel.id().asLongText(),serverType);
}else{
logger.error("channel is null");
}
}
/**
* 用户认证
* @param channel 连接
* @param userId 用户ID
* @param major 客户端主版本号
* @param minor 客户端次版本号
*/
public static synchronized void authChannel(Channel channel,String userId,Integer major,Integer minor){
logger.debug("Invoke authedChannels({},{},{},{})",channel.id().asLongText(),userId,major,minor);
major = major != null ? major : 0;
minor = minor != null ? minor : 0;
WrapperedChannel wrapperedChannel = rawChannels.get(channel);
if(wrapperedChannel != null){
wrapperedChannel.whenAuthed(userId,major,minor);
Set<WrapperedChannel> authedWchannelSet = authedChannels.computeIfAbsent(userId, k -> new HashSet<>());
authedWchannelSet.add(wrapperedChannel);
logger.debug("Authed channel {} with user {}", channel.id().asLongText(), userId);
}else{
logger.error("Authed channel,but wrappedChannel is null.");
}
}
/**
* 移除一个连接(Channel)
* @param channel 要移除的连接
*/
public static synchronized void removeChannel(Channel channel){
logger.debug("Invoke removeChannel({})",channel.id().asLongText());
WrapperedChannel wrapperedChannel = rawChannels.get(channel);
if(wrapperedChannel != null){
//从rawChannels集合中删除
rawChannels.remove(channel);
logger.debug("Remove a channel from rawChannels: {}",channel.id().asLongText());
if(wrapperedChannel.isAuthed()){
Set<WrapperedChannel> authedChannelSet = authedChannels.get(wrapperedChannel.getUserId());
//从authedChannel集合的value(set)中删除
if(CollectionUtil.isNotEmpty(authedChannelSet)){
authedChannelSet.remove(wrapperedChannel);
logger.debug("Remove a channel from authedChannelSet: {}, {}",wrapperedChannel.getUserId(),channel.id().asLongText());
}
//从authedChannel中删除,此处不用else,因为if中语句执行完毕之后,authedChannelSet也可能变成空集合
if(CollectionUtil.isEmpty(authedChannelSet)){
authedChannels.remove(wrapperedChannel.getUserId());
logger.debug("Remove a user from authedChannels: {}",wrapperedChannel.getUserId());
}
}
}else{
logger.error("Remove channel,but wrappedChannel is null.");
}
if(channel.isOpen() || channel.isActive()){
channel.close();
}
}
/**
* 移除一个用户
* @param userId 要移除的用户
*/
public static synchronized void removeUser(String userId){
logger.debug("Invoke remove user : {}",userId);
Set<WrapperedChannel> wChannelSet = authedChannels.get(userId);
if(CollectionUtil.isNotEmpty(wChannelSet)){
for(WrapperedChannel wChannel : wChannelSet){
//从rawChannel中依次删除
rawChannels.remove(wChannel.getChannel());
logger.debug("Remove a channel from rawChannels: {}",wChannel.getChannel().id().asLongText());
}
}
//从authedChannel中删除
authedChannels.remove(userId);
logger.debug("Remove a user from authedChannels: {}",userId);
}
/**
* 添加版本号
* 只能给已认证的请求添加版本号
* @param channel 连接
* @param major 主版本号
* @param minor 次版本号
*/
public static synchronized void versionChannel(Channel channel,int major,int minor){
logger.debug("Invoke Version channel({},{},{}))",channel.id().asLongText(),major,minor);
WrapperedChannel wChannel = rawChannels.get(channel);
if(wChannel != null){
wChannel.setVersion(major,minor);
logger.debug("Version Channel: {},{},{}",channel.id().asLongText(),major,minor);
}else{
logger.error("Remove channel,but wrappedChannel is null.");
}
}
/**
* 发送广播消息给所有已认证Channel
* @param message 消息
*/
public static synchronized void broadCastAuthed(Object message) {
logger.debug("Invoke broadCastAuthed({})",message);
for (Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()) {
WrapperedChannel wChannel = entry.getValue();
if(wChannel.isAuthed()) {
wChannel.writeAndFlush(message);
logger.debug("Send Message {} to {},{}",message,wChannel.getUserId(),wChannel.getId());
}
}
}
/**
* 发送广播消息
* @param message 消息
*/
public static synchronized void broadCast(Object message) {
logger.debug("Invoke broadCast({})",message);
for (Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()) {
entry.getValue().writeAndFlush(message);
logger.debug("Send Message {} to {}",message,entry.getValue().getId());
}
}
/**
* 发送消息给某个连接
* @param channel 连接
* @param message 消息
*/
public static synchronized void sendTo(Channel channel,Object message){
logger.debug("Invoke sendTo({},{})",channel.id().asLongText(),message);
WrapperedChannel wrapperedChannel = rawChannels.get(channel);
if(wrapperedChannel != null) {
wrapperedChannel.writeAndFlush(message);
logger.debug("Write message {} to Channel {}",message,channel);
}else{
logger.error("can't find channel from rawChannels");
}
}
/**
* 发送消息给某个用户
* @param userId 用户ID
* @param message 消息
*/
public static synchronized boolean sendTo(String userId,Object message){
logger.debug("Invoke sendTo({},{})",userId,message);
Set<WrapperedChannel> wChannelSet = authedChannels.get(userId);
if(CollectionUtil.isNotEmpty(wChannelSet)){
for(WrapperedChannel wChannel:wChannelSet){
wChannel.writeAndFlush(message);
logger.debug("Send message {} to channel {}",message,userId);
}
return true;
}else{
return false;
}
}
/**
* 刷新最后一次接收数据时间 每次接收数据需要调用该函数
* @param channel 接收数据的连接
*/
public static synchronized void flushReceiveTimestamp(Channel channel){
logger.debug("Invoke flushReceiveTimestamp({})",channel.id().asLongText());
WrapperedChannel wrapperedChannel = rawChannels.get(channel);
if(wrapperedChannel != null){
wrapperedChannel.whenReceivedData();
}else{
logger.error("can find channel from rawChannels");
}
}
/**
* 关闭所有未认证的连接
* @param unAuthedChannelsMaxAliveTimeInSeconds 未认证的最大时长(s)
* @throws Exception
* @deprecated 已过期该方法在多线程并发下可能出现问题建议使用messageService中的同名方法
*/
@Deprecated
public static synchronized void closeUnAuthedChannels(Long unAuthedChannelsMaxAliveTimeInSeconds) throws Exception {
logger.debug("Inovke closeUnAuthedChannels({})",unAuthedChannelsMaxAliveTimeInSeconds);
Iterator<Map.Entry<Channel,WrapperedChannel>> it = rawChannels.entrySet().iterator();
while(it.hasNext()){
Map.Entry<Channel,WrapperedChannel> entry = it.next();
WrapperedChannel wrapperedChannel = entry.getValue();
if((!wrapperedChannel.isAuthed()) && (wrapperedChannel.getConnectedSeconds() > unAuthedChannelsMaxAliveTimeInSeconds)) {
it.remove();
//关闭连接
wrapperedChannel.getChannel().close();
logger.debug("Remove a unAuthed Channel {}, which has connected {}s,maxOutTime is {}s", wrapperedChannel.getId(),wrapperedChannel.getConnectedSeconds() ,
unAuthedChannelsMaxAliveTimeInSeconds );
}
}
}
/**
* 判断Channel是否存在
* @param channel 连接
* @return 是否存在
*/
public static synchronized boolean channelExist(Channel channel){
return rawChannels.containsKey(channel);
}
/**
* 判断Channel是否已经认证
* @param channel 连接
* @return 是否认证
*/
public static synchronized boolean channelAuthed(Channel channel){
return rawChannels.containsKey(channel) && rawChannels.get(channel).isAuthed();
}
/**
* 判断用户是否在线
* @param userId 用户ID
* @return 是否认证
*/
public static synchronized boolean isUserOnline(String userId){
return authedChannels.containsKey(userId);
}
/**
* 根据Channel获取对应的用户ID
* @param channel 连接
* @return 用户ID
*/
public static synchronized String getUserIdByChannel(Channel channel) {
return rawChannels.containsKey(channel) ? rawChannels.get(channel).getUserId() : null;
}
/**
* 获取所有在线用户
* @return 所有用户的集合列表
*/
public static synchronized Set<String> getOnlineUsers(){
return authedChannels.keySet();
}
/**
* 获取某种类型的所有在线用户的连接
* @param type 客户端类型(ws/tcp modebus/tcp text)
* @return 所有在西安channel的集合列表
*/
public static synchronized Set<Channel> getOnlineChannels(String type){
Set<Channel> onLineChannels = CollectionUtil.newHashSet();
for(Map.Entry<Channel,WrapperedChannel> entry: rawChannels.entrySet()){
WrapperedChannel wrapperedChannel = entry.getValue();
if(wrapperedChannel.isAuthed() && wrapperedChannel.getType().equals(type)){
onLineChannels.add(entry.getKey());
}
}
return onLineChannels;
}
/**
* 获取一个rawChannel的副本
* @return 副本
*/
public static Map<Channel, WrapperedChannel> getCopyOfAllChannels() {
Map<Channel,WrapperedChannel> copyMap = new HashMap<>(rawChannels.size());
copyMap.putAll(rawChannels);
return copyMap;
}
/**
* 根据Channel获取WrapperedChannel
* @param channel channel
* @return 对应的wrapperedChannel
*/
public static WrapperedChannel getWrapperedChannelByChannel(Channel channel) {
return rawChannels.get(channel);
}
public static synchronized void showAuthedChannels(){
for(Map.Entry<String,Set<WrapperedChannel>> entry : authedChannels.entrySet()){
for (WrapperedChannel channel:entry.getValue()){
logger.debug("{}-->{}",entry.getKey(),channel.toString());
}
}
}
public static synchronized void showAllChannels(){
for(Map.Entry<Channel,WrapperedChannel> entry : rawChannels.entrySet()){
logger.debug(entry.getValue().toString());
}
}
public static Set<String> getAllOnlineUsers() {
return authedChannels.keySet();
}
}

200
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/WrapperedChannel.java

@ -1,200 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty;
import cn.hutool.core.util.ObjectUtil;
import wiki.tall.ccmq.common.util.DateUtil;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import java.net.InetSocketAddress;
/**
* @author wei
*/
@Getter
@Setter
public class WrapperedChannel {
@Getter
@Setter
private static class ClientVersion{
int major;
int minor;
public ClientVersion(int major,int minor){
this.major = major;
this.minor = minor;
}
}
/**
* Netty's Channel
*/
private Channel channel;
/**
* userId
*/
private String userId;
/**
* 客户端版本号
*/
private ClientVersion version;
/**
* 客户端类型(哪种服务器)
*/
private String type;
/**
* 是否认证
*/
private boolean authed;
/**
* 连接建立时间
*/
private Long createdAtInSeconds;
/**
* 认证时间(s)
*/
private Long authedAtInSeconds;
/**
* 最后一次接收有效数据时间(包括心跳包)
*/
private Long lastDataReceivedInSeconds;
/**
* 最后一次发送有效数据时间(包括心跳包)
*/
private Long lastDataSendInSeconds;
/**
* 接收到数据条数(每接收到一条协议加1包括心跳)
*/
private long dataReceivedCount;
/**
* 发送数据条数(每发送到一条协议加1包括心跳)
*/
private long dataSendCount;
public WrapperedChannel(Channel channel){
this.channel = channel;
this.createdAtInSeconds = DateUtil.currentSeconds();
}
public WrapperedChannel(Channel channel,String type){
this(channel);
this.type = type;
}
public WrapperedChannel(Channel channel,String type,int major,int minor){
this(channel,type);
this.version = new ClientVersion(major,minor);
}
public String getVersion(){
if(ObjectUtil.isNotNull(version)){
return "v" + version.getMajor() + "." + version.getMinor();
}
return null;
}
public void setVersion(int major,int minor){
if (ObjectUtil.isNull(version)) {
version = new ClientVersion(major, minor);
} else {
version.setMajor(major);
version.setMinor(minor);
}
}
public String getRemoteAddr(){
if(ObjectUtil.isNotNull(channel)){
InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
if(ObjectUtil.isNotNull(insocket)) {
return insocket.getAddress().getHostAddress();
}
}
return null;
}
public Integer getRemotePort(){
if(ObjectUtil.isNotNull(channel)){
InetSocketAddress insocket = (InetSocketAddress) channel.remoteAddress();
if(ObjectUtil.isNotNull(insocket)) {
return insocket.getPort();
}
}
return null;
}
public String getId(){
if(ObjectUtil.isNotNull(channel)){
return channel.id().asLongText();
}
return null;
}
public void whenReceivedData(){
lastDataReceivedInSeconds = DateUtil.currentSeconds();
dataReceivedCount++;
}
public void whenSendData(){
lastDataSendInSeconds = DateUtil.currentSeconds();
dataSendCount++;
}
public void whenAuthed(){
authed = true;
authedAtInSeconds = DateUtil.currentSeconds();
}
public void whenAuthed(String userId,int major,int minor){
whenAuthed();
setVersion(major,minor);
this.userId = userId;
}
public void writeAndFlush(Object message){
if(channel != null && channel.isActive()){
channel.writeAndFlush(message);
whenSendData();
}
}
public Long getConnectedSeconds(){
return createdAtInSeconds == null ? null : DateUtil.currentSeconds() - createdAtInSeconds;
}
public Long getOnlineSeconds(){
return authedAtInSeconds == null ? null : DateUtil.currentSeconds() - authedAtInSeconds ;
}
@Override
public boolean equals(Object obj) {
if(ObjectUtil.isNull(obj)) {
return false;
}
if(this == obj) {
return true;
}
if(ObjectUtil.isNull(channel)) {
return false;
}
if(obj.getClass() == this.getClass()){
WrapperedChannel other = (WrapperedChannel)obj;
return channel.equals(other.channel);
}else if(obj.getClass() == channel.getClass()){
Channel other = (Channel)obj;
return channel.equals(other);
}
return false;
}
@Override
public int hashCode() {
if(ObjectUtil.isNotNull(channel)){
return channel.hashCode();
}
return 0;
}
@Override
public String toString() {
return String.format("id: %s, type: %s, authed: %b, userId: %s, version: %s",getId(),type,authed,userId,getVersion());
}
}

115
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusConverter.java

@ -1,115 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver;
public class ModbusConverter {
// public static BaseMessageDto convertCCModbusToMessage(CCModBusEntity ccModBusEntity) {
// BaseMessageDto message = null;
// int addr = ccModBusEntity.getAddr() & 0xFF;
// int oper = ccModBusEntity.getOper() & 0xFF;
// WebConstant.Message_Type type = WebConstant.Message_Type.valueOf(addr);
// if(type != null) {
// switch (type) {
// case Heart: {
// message = toHeartMessage(addr,oper,ccModBusEntity.getOriginData());
// break;
// }
// case Auth:{
// message = toAuthMessage(addr,oper,ccModBusEntity.getOriginData());
// }
// }
// }
// return message;
// }
//
// /**
// * OriginaData ==> 0x00
// * @param oper
// * @return
// */
// private static BaseMessageDto toHeartMessage(int addr, int oper, byte[] originData){
// HeartMessageDto message = new HeartMessageDto();
// return message;
// }
//
// /**
// * OriginaData ==> 0x00 0x00 0x00 0x64
// * @param oper
// * @return
// */
// private static BaseMessageDto toAuthMessage(int addr, int oper, byte[] originData){
// Long userId = 0L;
// for(int i=0;i<originData.length;i++){
// userId <<= 8;
// userId |= originData[i] & 0xFF;
// }
// AuthMessageDto message = new AuthMessageDto(userId,null);
// return message;
// }
//
// public static CCModBusEntity convertCommonProtocolToCCModbus(BaseMessageDto message) {
// CCModBusEntity ccModBusEntity = null;
// WebConstant.Message_Type type = WebConstant.Message_Type.phaseOf(message.getType());
// switch (type){
// case Heart:{
// ccModBusEntity = fromHeartMessage((HeartMessageDto)message);
// break;
// }
// case Timer:{
// WebConstant.Message_Timer_Event event = WebConstant.Message_Timer_Event.phaseOf(message.getEvent());
// switch (event){
// case CountDown:
// ccModBusEntity = fromTimerMessageWithCountDown((TimerMessageWithCountDownDto)message);
// break;
// case Clock:
// ccModBusEntity = fromTimerMessageWithClock((TimerMessageWithClockDto)message);
// break;
// }
// break;
// }
// }
// return ccModBusEntity;
// }
//
// /**
// * OriginalData: 0x00
// * @param message
// * @return
// */
// private static CCModBusEntity fromHeartMessage(HeartMessageDto message){
// byte []originData = new byte[1];
// originData[0] = 0x00;
// byte addr = (byte)(WebConstant.Message_Type.phaseOf(message.getType()).value & 0xFF);
// byte oper = (byte)(WebConstant.Message_Type.phaseOf(message.getEvent()).value & 0xFF);
// CCModBusEntity ccModBusEntity = new CCModBusEntity(addr,oper,originData);
// return ccModBusEntity;
// }
//
// /**
// * originaData: a1 xx xx xx crc
// * @param message
// * @return
// */
// private static CCModBusEntity fromTimerMessageWithCountDown(TimerMessageWithCountDownDto message){
// Long second = message.getData().getSecond();
// byte []originData = new byte[5];
// originData[0] = (byte)(0xa1 & 0xFF);
// originData[1] = (0x48 & 0xFF);
// originData[2] = (byte)((second >> 8) & 0xFF);
// originData[3] = (byte)(second & 0xFF);
// originData[4] = (byte)((originData[0] + originData[1] + originData[2] + originData[3]) & 0xFF);
//
// byte addr = (byte)(WebConstant.Message_Type.phaseOf(message.getType()).value & 0xFF);
// byte oper = (byte)(WebConstant.Message_Timer_Event.phaseOf(message.getEvent()).value & 0xFF);
// CCModBusEntity ccModBusEntity = new CCModBusEntity(addr,oper,originData);
// return ccModBusEntity;
// }
//
// /**
// * originaData: xxx
// * @param message
// * @return
// */
// private static CCModBusEntity fromTimerMessageWithClock(TimerMessageWithClockDto message){
// return null;
// }
}

45
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusDecoder.java

@ -1,45 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
public class ModbusDecoder extends ByteToMessageDecoder {
private void discardNBytes(ByteBuf in, int length) {
for (int i = 0; i < length; i++) {
in.readByte();
}
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
// if (in.readableBytes() < CCModBusEntity.SIZE_MIN) {
// //长度小于协议的最小长度,继续读下一次
// return;
// }
//
// CCModBusEntity ccModBusEntity = new CCModBusEntity(in);
// CCModBusEntity.Error error = ccModBusEntity.valid();
// switch (error) {
// case ERROR_FILTER_NOT_MATCH:
// case ERROR_LEN_EXCLUEE_MAX:
// case ERROR_CRC_INVALID: //丢弃一个字节,继续读取
// discardNBytes(in, 1);
// return;
// case ERROR_NEED_MORE_DATA: //继续读取
// return;
// case ERROR_NONE:
// break;
// }
//
// //交给下个handler处理
// discardNBytes(in, ccModBusEntity.getModbusLength());
// BaseMessageDto message = ModbusConverter.convertCCModbusToMessage(ccModBusEntity);
// if(ObjectUtil.isNotNull(message)) {
// out.add(message);
// }
}
}

33
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusEncoder.java

@ -1,33 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver;
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class ModbusEncoder extends MessageToByteEncoder<OutMessageSet> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, OutMessageSet message, ByteBuf out) throws Exception {
//
// //1.处理Start->Timer[Countdown]
// if(message != null && message instanceof SyncMessageWithStartDto){
// SyncMessageWithStartDto startMessage = (SyncMessageWithStartDto)message;
// if(ObjectUtil.isNotNull(startMessage.getData().getBeginTaskId())){
// Long duration = startMessage.getData().getDuration();
// message = new TimerMessageWithCountDownDto(duration/1000,null,null);
// }
// }
//
// if(message != null){
// CCModBusEntity ccModBusEntity = ModbusConverter.convertCommonProtocolToCCModbus(message);
// if(ccModBusEntity != null){
// byte[] modbusData = ccModBusEntity.getModbusData();
// for(int i=0;i<modbusData.length;i++){
// System.out.printf("%02x ",modbusData[i]);
// }
// System.out.println();
// out.writeBytes(modbusData);
// }
// }
}
}

109
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/ModbusHandler.java

@ -1,109 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage;
import com.ccsens.ccmq.lowlevel.message.client.UnExceptedErrorMessage;
import com.ccsens.ccmq.lowlevel.client.ClientManager;
import com.ccsens.ccmq.lowlevel.message.MessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wiki.tall.ccmq.common.util.WebConstant;
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;
/**
* @author __zHangSan
*/
@ChannelHandler.Sharable
@Component
public class ModbusHandler extends SimpleChannelInboundHandler<InMessage> {
private Logger logger = LoggerFactory.getLogger(ModbusHandler.class);
private static final String TYPE = WebConstant.NETTY_SERVER_TYPE.TCP_MB.phase;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.addChannel(ctx.channel(),TYPE);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.removeChannel(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage())));
}catch(Exception e){
e.printStackTrace();
logger.error("TcpHex exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
cause.printStackTrace();
ctx.close();
logger.error("Channel closed. TcpHex get a exception: {}", cause.getMessage());
}
@SuppressWarnings("all")
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
if (idleStateEvent.state() == IdleState.READER_IDLE) {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage()));
}catch(Exception e){
e.printStackTrace();
logger.error("TcpHex exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
ctx.channel().close();
logger.error("TcpHex channel idle,closed.");
}
} else {
super.userEventTriggered(ctx, evt);
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception {
try {
//0.设置最后一次收数据的时间 和 当前线程channel
ChannelManager.flushReceiveTimestamp(ctx.channel());
ChannelManager.setCurrentChannel(ctx.channel());
//1.填充缺省信息
ClientManager.fillMessageInfo(inMessage, MessageConstant.DomainType.User);
//2.处理消息
MessageHandler.handleMessage(inMessage);
} catch (Exception e) {
e.printStackTrace();
logger.error("TcpHex Process Message Failed: {},{}", e.getMessage(), inMessage);
throw e;
} finally {
ChannelManager.removeCurrentChannel();
}
}
}

59
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcphexserver/NettyMBServer.java

@ -1,59 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcphexserver;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @author __zHangSan
*/
@Component
public class NettyMBServer {
@Autowired
private ModbusHandler modbusHandler;
private static final short SERVER_PORT = 8195;
@Async
public void start() {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new IdleStateHandler(40, 0, 0, TimeUnit.SECONDS));
p.addLast(new ModbusDecoder());
p.addLast(new ModbusEncoder());
p.addLast(modbusHandler);
}
});
// Start the server.
ChannelFuture f = b.bind(SERVER_PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} catch(Exception e){
e.printStackTrace();
}finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

61
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/NettyTextServer.java

@ -1,61 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class NettyTextServer {
@Autowired
private TextHandler textHandler;
@Autowired
private static final String DELIMITER = "_$$_";
private static final short SERVER_PORT = 8199;
@Async
public void start() {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new IdleStateHandler(40, 0, 0, TimeUnit.SECONDS));
p.addLast(new DelimiterBasedFrameDecoder(2048, Unpooled.copiedBuffer(DELIMITER.getBytes())));
p.addLast(new TextDecoder());
p.addLast(new TextEncoder(2048,Unpooled.copiedBuffer(DELIMITER.getBytes())));
p.addLast(textHandler);
}
});
// Start the server.
ChannelFuture f = b.bind(SERVER_PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} catch(Exception e){
e.printStackTrace();
}finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

32
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextDecoder.java

@ -1,32 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import wiki.tall.ccmq.common.util.JacksonUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
/**
* @author __zHangSan
*/
public class TextDecoder extends MessageToMessageDecoder<ByteBuf> {
private static Logger logger = LoggerFactory.getLogger(TextDecoder.class);
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List<Object> out) throws Exception {
String msgString = msg.toString(Charset.defaultCharset());
logger.info("TcpText received: {}" + msgString);
try {
out.add(JacksonUtil.jsonToBean(msgString, InMessage.class));
}catch (IOException e){
e.printStackTrace();
logger.error("TcpText read error: {}",msgString);
}
}
}

49
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextEncoder.java

@ -1,49 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver;
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet;
import wiki.tall.ccmq.common.util.JacksonUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.List;
/**
* @author __zHangSan
*/
public class TextEncoder extends MessageToMessageEncoder<OutMessageSet> {
private final Charset charset;
private int maxLength;
private ByteBuf delimiter;
public TextEncoder() {
this(Charset.defaultCharset());
}
public TextEncoder(Charset charset) {
if (charset == null) {
throw new NullPointerException("charset");
} else {
this.charset = charset;
}
}
public TextEncoder(int maxLength, ByteBuf delimiter) {
this();
this.maxLength = maxLength;
this.delimiter = delimiter;
}
@Override
protected void encode(ChannelHandlerContext ctx, OutMessageSet outMessageSet, List<Object> out) throws Exception {
String delimiterString = delimiter.toString(Charset.defaultCharset());
String msgString = JacksonUtil.beanToJson(outMessageSet) + delimiterString;
if (msgString.length() != 0) {
out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msgString), this.charset));
}
}
}

110
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/tcptextserver/TextHandler.java

@ -1,110 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.tcptextserver;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage;
import com.ccsens.ccmq.lowlevel.message.client.UnExceptedErrorMessage;
import com.ccsens.ccmq.lowlevel.client.ClientManager;
import com.ccsens.ccmq.lowlevel.message.MessageHandler;
import wiki.tall.ccmq.common.config.SettingProps;
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author __zHangSan
*/
@ChannelHandler.Sharable
@Component
public class TextHandler extends SimpleChannelInboundHandler<InMessage> {
private static Logger logger = LoggerFactory.getLogger(TextHandler.class);
@Autowired
private SettingProps settingProps;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.addChannel(ctx.channel(),settingProps.getNettyTcpTextType());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.removeChannel(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage())));
}catch(Exception e){
e.printStackTrace();
logger.error("TcpText exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
cause.printStackTrace();
ctx.close();
logger.error("Channel closed. TcpText get a exception: {}",cause.getMessage());
}
@SuppressWarnings("all")
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
if (idleStateEvent.state() == IdleState.READER_IDLE) {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage()));
}catch(Exception e){
e.printStackTrace();
logger.error("TcpText exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
ctx.channel().close();
logger.error("TcpText channel idle,closed.");
}
} else {
super.userEventTriggered(ctx, evt);
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception {
try {
//0.设置最后一次收数据的时间 和 当前线程channel
ChannelManager.flushReceiveTimestamp(ctx.channel());
ChannelManager.setCurrentChannel(ctx.channel());
//1.填充缺省信息
ClientManager.fillMessageInfo(inMessage, MessageConstant.DomainType.User);
//2.处理消息
MessageHandler.handleMessage(inMessage);
} catch (Exception e) {
e.printStackTrace();
logger.error("TcpText Process Message Failed: {},{}", e.getMessage(), inMessage);
throw e;
} finally {
ChannelManager.removeCurrentChannel();
}
}
}

72
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/NettyWsServer.java

@ -1,72 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.wsserver;
import wiki.tall.ccmq.common.config.SettingProps;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @author wei
*/
@Component
public class NettyWsServer {
@Autowired
private SettingProps settingProps;
@Autowired
private WebSocketHandler webSocketHandler;
@Async
public void start() {
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if(settingProps.getKeepAlive().isEnable()) {
p.addLast(new IdleStateHandler(settingProps.getKeepAlive().getMaxIdleSeconds(),
0, 0, TimeUnit.SECONDS));
}
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(64 * 1024));
p.addLast(new ChunkedWriteHandler());
p.addLast(new WebSocketServerProtocolHandler(settingProps.getNettyWsUri()));
p.addLast(new WebSocketDecoder());
p.addLast(new WebSocketEncoder());
p.addLast(webSocketHandler);
}
});
// Start the server.
ChannelFuture f = b.bind(settingProps.getNettyWsPort()).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} catch(Exception e){
e.printStackTrace();
}finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

33
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketDecoder.java

@ -1,33 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.wsserver;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import wiki.tall.ccmq.common.util.JacksonUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
/**
* @author wei
*/
public class WebSocketDecoder extends MessageToMessageDecoder<TextWebSocketFrame> {
private static Logger logger = LoggerFactory.getLogger(WebSocketDecoder.class);
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame msg, List<Object> out) throws Exception {
String text = msg.text();
logger.info("Websocket received: {}",text);
try {
out.add(JacksonUtil.jsonToBean(text, InMessage.class));
}catch(IOException e){
e.printStackTrace();
logger.error("Websocket Read Error: {}",text);
throw e;
}
}
}

25
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketEncoder.java

@ -1,25 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.wsserver;
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet;
import wiki.tall.ccmq.common.util.JacksonUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author wei
*/
public class WebSocketEncoder extends MessageToByteEncoder<OutMessageSet> {
private static Logger logger = LoggerFactory.getLogger(WebSocketEncoder.class);
@Override
protected void encode(ChannelHandlerContext ctx, OutMessageSet outMessageSet, ByteBuf out) throws Exception {
String msg = JacksonUtil.beanToJson(outMessageSet);
ctx.writeAndFlush(new TextWebSocketFrame(msg));
logger.info("Websocket send: {}",msg);
}
}

107
src/main/java/com/ccsens/ccmq/lowlevel/client/netty/wsserver/WebSocketHandler.java

@ -1,107 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.netty.wsserver;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.client.ClientIdleClosedMessage;
import com.ccsens.ccmq.lowlevel.message.client.UnExceptedErrorMessage;
import com.ccsens.ccmq.lowlevel.client.ClientManager;
import com.ccsens.ccmq.lowlevel.message.MessageHandler;
import wiki.tall.ccmq.common.config.SettingProps;
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author wei
*/
@ChannelHandler.Sharable
@Component
public class WebSocketHandler extends SimpleChannelInboundHandler<InMessage> {
private static Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
@Autowired
private SettingProps settingProps;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.addChannel(ctx.channel(),settingProps.getNettyWsType());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ChannelManager.removeChannel(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new UnExceptedErrorMessage(cause.getMessage())));
}catch(Exception e){
e.printStackTrace();
logger.error("Ws exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
cause.printStackTrace();
ctx.close();
logger.error("Channel closed. Ws get a exception: {}", cause.getMessage());
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
if (idleStateEvent.state() == IdleState.READER_IDLE) {
try {
ChannelManager.setCurrentChannel(ctx.channel());
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.User,new ClientIdleClosedMessage()));
}catch(Exception e){
e.printStackTrace();
logger.error("Ws exceptionCaught handler error: {}",e.getMessage());
}finally {
ChannelManager.removeCurrentChannel();
}
ctx.channel().close();
logger.error("Ws channel idle,closed.");
}
} else {
super.userEventTriggered(ctx, evt);
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, InMessage inMessage) throws Exception {
try {
//0.设置最后一次收数据的时间 和 当前线程channel
ChannelManager.flushReceiveTimestamp(ctx.channel());
ChannelManager.setCurrentChannel(ctx.channel());
//1.填充缺省信息
ClientManager.fillMessageInfo(inMessage,MessageConstant.DomainType.User);
//2.处理消息
MessageHandler.handleMessage(inMessage);
} catch (Exception e) {
e.printStackTrace();
logger.error("Websocket Process Message Failed: {},{}", e.getMessage(), inMessage);
throw e;
} finally {
ChannelManager.removeCurrentChannel();
}
}
}

36
src/main/java/com/ccsens/ccmq/lowlevel/client/rabbitmq/QueueManager.java

@ -1,36 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.rabbitmq;
import wiki.tall.ccmq.common.config.SettingProps;
import wiki.tall.ccmq.common.util.JacksonUtil;
import wiki.tall.ccmq.common.util.SpringContextUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
/**
* @author __zHangSan
*/
public class QueueManager {
private static Logger logger = LoggerFactory.getLogger(QueueManager.class);
public static String getInQueueName(){
return SpringContextUtils.getBean(SettingProps.class).getMq().getInName();
}
public static String getOutQueueName(){
return SpringContextUtils.getBean(SettingProps.class).getMq().getOutName();
}
public static void sendTo(String queueName,Object obj) throws JsonProcessingException {
String text = JacksonUtil.beanToJson(obj);
logger.info("QueueManager SendTo: {}",text);
SpringContextUtils.getBean(RabbitTemplate.class).convertAndSend(queueName,text);
}
public static void sendTo(Object obj) throws JsonProcessingException {
String text = JacksonUtil.beanToJson(obj);
logger.info("QueueManager SendTo: {}",text);
SpringContextUtils.getBean(RabbitTemplate.class).convertAndSend(getOutQueueName(),text);
}
}

48
src/main/java/com/ccsens/ccmq/lowlevel/client/rabbitmq/RabbitMqListener.java

@ -1,48 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.rabbitmq;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.client.UnExceptedErrorMessage;
import com.ccsens.ccmq.lowlevel.client.ClientManager;
import com.ccsens.ccmq.lowlevel.message.MessageHandler;
import wiki.tall.ccmq.common.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* @author wei
*/
@Component
@PropertySource(value = {"classpath:setting-${spring.profiles.active}.properties"})
@RabbitListener(queues = "${setting.mq.inName}")
public class RabbitMqListener {
private Logger logger = LoggerFactory.getLogger(RabbitMqListener.class);
@RabbitHandler
public void process(String messageJson) {
logger.info("Rabbit Received: {}",messageJson);
try {
InMessage inMessage = JacksonUtil.jsonToBean(messageJson, InMessage.class);
//1.填充缺省字段
ClientManager.fillMessageInfo(inMessage,MessageConstant.DomainType.Queue);
//2.处理消息
MessageHandler.handleMessage(inMessage);
}catch (Exception e){
e.printStackTrace();
try {
MessageHandler.handleMessage(
InMessage.newToServerMessage(MessageConstant.DomainType.Queue,new UnExceptedErrorMessage(e.getMessage())));
} catch (Exception ex) {
ex.printStackTrace();
}
logger.error("Rabbit Process Message Failed: {},{}",e.getMessage(),messageJson);
}
}
}

28
src/main/java/com/ccsens/ccmq/lowlevel/client/restful/MessageApi.java

@ -1,28 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.restful;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import wiki.tall.ccmq.common.util.JsonResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class MessageApi {
/**
* Restful Json 请求
* @param request
* @param response
* @param model
* @return
* @throws Exception
*/
@RequestMapping(value = "/json",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
@ResponseBody
public JsonResponse api(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
return JsonResponse.newInstance().ok();
}
}

16
src/main/java/com/ccsens/ccmq/lowlevel/client/restful/RestManager.java

@ -1,16 +0,0 @@
package com.ccsens.ccmq.lowlevel.client.restful;
import com.ccsens.ccmq.lowlevel.message.common.OutMessageSet;
/**
* @author __zHangSan
*/
public class RestManager {
public static void sendTo(OutMessageSet outMessageSet){
}
public static String getOutRestName(){
return "";
}
}

481
src/main/java/com/ccsens/ccmq/lowlevel/message/MessageHandler.java

@ -1,481 +0,0 @@
package com.ccsens.ccmq.lowlevel.message;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import com.ccsens.ccmq.lowlevel.client.ClientManager;
import com.ccsens.ccmq.lowlevel.client.rabbitmq.QueueManager;
import com.ccsens.ccmq.lowlevel.message.server.InvertedMessage;
import com.ccsens.ccmq.lowlevel.persist.IMessageDao;
import com.ccsens.ccmq.lowlevel.message.client.*;
import com.ccsens.ccmq.lowlevel.message.common.*;
import com.ccsens.ccmq.lowlevel.message.server.ChannelStatusMessage;
import com.ccsens.ccmq.lowlevel.message.server.PongMessage;
import com.ccsens.ccmq.lowlevel.message.server.ServerAckMessage;
import com.ccsens.ccmq.lowlevel.client.netty.ChannelManager;
import com.ccsens.ccmq.lowlevel.client.netty.WrapperedChannel;
import com.ccsens.ccmq.lowlevel.service.IUserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import wiki.tall.ccmq.common.config.SettingProps;
import wiki.tall.ccmq.common.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author __zHangSan
*/
@Component
public class MessageHandler {
private Logger logger = LoggerFactory.getLogger(MessageHandler.class);
/**
* 发送消息最大等待ack时间
*/
private static final Integer REDIS_ACK_EXPIRED_SECONDS = 10;
/**
* 每次最多查询消息条数
*/
private static final Integer MAX_MESSAGE_NUM = 50;
private static IMessageDao getMessageDao(){
return SpringContextUtils.getBean(IMessageDao.class);
}
private static SettingProps getSettingProps(){
return SpringContextUtils.getBean(SettingProps.class);
}
private static IUserService getUserService(){
return SpringContextUtils.getBean(IUserService.class);
}
/**
* 每个20s同步一次有未决消息的用户到pendingClientSet中
* @throws Exception
*/
@Scheduled(cron = "0/20 * * * * *")
public void detectPendingClients() throws Exception{
logger.info("schedule detectPendingClients: {}", DateUtil.currentSeconds());
//1.获取所有的在线客户端
Set<String> onLineUsers = ChannelManager.getAllOnlineUsers();
Set<String> onLineClients = new HashSet<>(onLineUsers.size()+1);
if(CollectionUtil.isNotEmpty(onLineUsers)) {
for (String onlineUser : onLineUsers) {
onLineClients.add(CcMessageUtil.getDomainTypeAndUserIdString(MessageConstant.DomainType.User, onlineUser));
}
}
onLineClients.add(CcMessageUtil.getDomainTypeAndUserIdString(MessageConstant.DomainType.Queue,
QueueManager.getOutQueueName()));
//2.如果有未ack消息并且不在当前待处理列表中,则添加
for(String domainTypeAndUserId: onLineClients){
addClientToRedisUnPendingSet(domainTypeAndUserId);
}
}
/**
* 每隔5分钟将所有的tempSucceed的消息状态修改为pending
* @throws Exception
*/
@Scheduled(cron = "0 */5 * * * *")
public void updateMessageTempStatusToPending() throws Exception{
logger.info("schedule updateMessageTempStatusToPending: {}", DateUtil.currentSeconds());
getMessageDao().updateMessageStatusToPending();
}
/**
* 每隔5分钟扫描消息过期或发送次数达到上限消息
* @throws Exception
*/
@Scheduled(cron = "0 */5 * * * *")
public void updateMessageExpiredStatus() throws Exception{
logger.info("schedule updateMessageExpiredStatus: {}", DateUtil.currentSeconds());
getMessageDao().updateMessageToExpired();
}
/**
* 每隔5分钟扫描消息过期或发送次数达到上限消息
* @throws Exception
*/
@Scheduled(cron = "0 */5 * * * *")
public void updateMessageSendTimesUpLimitStatus() throws Exception{
logger.info("schedule updateMessageSendTimesUpLimitStatus: {}", DateUtil.currentSeconds());
getMessageDao().updateMessageToSendTimesUpLimit();
}
@Async("cc-msg-executor")
public void loopSendMessage() throws Exception {
String typeAndUserId = null,ackId = null;
List<Message> messageList = null;
List<String> sendTimesUpLimitMessageList = null,expiredMessageList = null;
while(true){
//从redis中或取第一个待处理用户
Object o = RedisUtil.sPop(RedisKeyManager.getPendingClientSetKey());
if(o != null && StrUtil.isNotEmpty(typeAndUserId = ackId = (String)o)){
Console.log("RedisUtil.sPop: {}",o);
String []stringArray = CcMessageUtil.splitTypeAndUserId(typeAndUserId);
MessageConstant.DomainType toDomain = MessageConstant.DomainType.valueOf(stringArray[0]);
String to = stringArray[1];
//针对同一用户,在上一次ack还未收到/超时之前,不进行处理
Object lockObj = ResourceLock.getLockObj(ackId);
synchronized (lockObj) {
//查找该用户是否有正在处理的消息
if (RedisUtil.hasKey(RedisKeyManager.getAckSetKey(ackId))) {
//将当前用户重新放回到待处理列表的最后
RedisUtil.sSet(RedisKeyManager.getPendingClientSetKey(), o);
continue;
}
//查找所有没有ack的消息
messageList = getMessageDao().getClientPendingMessage(toDomain, to, MAX_MESSAGE_NUM);
if (CollectionUtil.isEmpty(messageList)) {
continue;
}
//判断用户是否在线
boolean clientOnLine = ClientManager.isUserOnline(toDomain,to);
//发送和处理消息
if(clientOnLine){
//发送
//1.手机所有待发送的messageId,以ackId为key放入redis
Set<String> messageIdSet = new HashSet<>(messageList.size());
OutMessageSet outMessageSet = OutMessageSet.newInstance();
for(Message message : messageList) {
messageIdSet.add(message.getId());
outMessageSet.add(new OutMessage(message));
getMessageDao().incrementSendTimes(message.getId(), DateUtil.currentSeconds());
}
RedisUtil.sSetAndTime(RedisKeyManager.getAckSetKey(ackId),REDIS_ACK_EXPIRED_SECONDS,messageIdSet.toArray());
//2.构造outMessage并且发送
outMessageSet.ackId(ackId);
//发送给对应的接收者
ClientManager.sendMessageToAuthedClient(toDomain,to,outMessageSet);
}else{
//不发送,根据规则检查所有“offLineDiscard”的消息设置为failed状态
for(Message message : messageList){
if(message.getRule().getOfflineDiscard() == 1){
getMessageDao().updateMessageStatus(message.getId(),MessageConstant.Status.Failed);
}
}
}
}
ResourceLock.freeLockObj(ackId);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void handleMessage(InMessage inMessage) throws Exception{
//1. 处理发送给Server的消息
if(inMessage.getToDomain() == MessageConstant.DomainType.Server){
String type = null;
JsonNode typeNode = JacksonUtil.getJsonProperty(inMessage.getData(), "type");
//校验错误
if (typeNode == null || StrUtil.isEmpty(type = typeNode.textValue())) {
ClientManager.sendServerAck(inMessage, MessageConstant.Error.MessageNoTypeError);
}
//处理消息
handlerServerMessage(type,inMessage);
return;
}
//2. 处理发送给Client的消息
//校验错误
if (CollectionUtil.isEmpty(inMessage.getTos())) {
ClientManager.sendServerAck(inMessage, MessageConstant.Error.MessageNoReceiversError);
return;
}
if(!ClientManager.isSenderAuthed(inMessage)){
ClientManager.sendServerAck(inMessage,MessageConstant.Error.UnAuthed);
ClientManager.closeCurrentSender(inMessage);
return;
}
//存储消息(原始消息+按照tos拆分之后的每条消息)
saveMessage(inMessage);
//3.发送ack
ClientManager.sendServerAck(inMessage, MessageConstant.Error.Ok);
//4.将用户添加到Redis待处理用户集合中
RedisUtil.sSet(RedisKeyManager.getPendingClientSetKey(),
CcMessageUtil.getNameManglingToList(inMessage).toArray()
);
}
private static void saveMessage(InMessage inMessage) throws Exception{
//1.存储原始消息
getMessageDao().saveOrUpdateMessage(inMessage);
//2.按照tos拆分成不同消息
for(String to : inMessage.getTos()){
Message message = new Message(inMessage,to);
getMessageDao().saveOrUpdateMessage(message);
}
}
private static void handlerServerMessage(String type, InMessage inMessage) throws Exception {
MessageConstant.ClientMessageType clientMessageType = MessageConstant.ClientMessageType.valueOf(type);
String data = inMessage.getData();
OutMessage outMessage = null;
//1. 处理消息
switch(clientMessageType){
case Ping: {
PingMessage inSysData = JacksonUtil.jsonToBean(data, PingMessage.class);
if (null != inSysData.getData()) {
ChannelManager.versionChannel(ChannelManager.getCurrentChannel(), inSysData.getData().getMajor(), inSysData.getData().getMinor());
}
outMessage = new OutMessage(JacksonUtil.beanToJson(new PongMessage()));
break;
}
case Auth: {
boolean authSuccess = false;
AuthMessage inSysData = JacksonUtil.jsonToBean(data, AuthMessage.class);
if(null != inSysData.getData()){
if(StrUtil.isNotEmpty(inSysData.getData().getToken())) {
String userId = getUserService().getUserIdByToken(inSysData.getData().getToken());
if(StrUtil.isNotEmpty(userId)){
ChannelManager.authChannel(ChannelManager.getCurrentChannel(),userId,inSysData.getData().getMajor(),inSysData.getData().getMinor());
onClientOnLine(MessageConstant.DomainType.User,userId);
authSuccess = true;
}
}
}
if(!authSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ChannelStatusMessage(false,0L,MessageConstant.Error.AuthFailed))
);
}else{
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ChannelStatusMessage(true,0L,MessageConstant.Error.Ok))
);
}
break;
}
case GetChannelStatus: {
WrapperedChannel wrapperedChannel = ChannelManager.getWrapperedChannelByChannel(ChannelManager.getCurrentChannel());
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ChannelStatusMessage(wrapperedChannel.isAuthed(),
wrapperedChannel.getOnlineSeconds(),
MessageConstant.Error.Ok))
);
break;
}
case Ack: {
//根据id找到
boolean ackSuccess = false;
AckMessage inSysData = JacksonUtil.jsonToBean(data, AckMessage.class);
if(null != inSysData.getData()){
updateMessageAckStatus(inSysData.getData().getAckId());
ackSuccess = true;
}
if(!ackSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.AckParameterError))
);
}
//没有错误不回复消息
break;
}
case HasRead: {
boolean hasReadSuccess = false;
HasReadMessage inSysData = JacksonUtil.jsonToBean(data, HasReadMessage.class);
if(null != inSysData.getData()){
updateMessageHasReadStatus(
inSysData.getData().getFromDomain(),inSysData.getData().getFromUserId(),
inMessage.getFromDomain().name(),inMessage.getFrom(), inSysData.getData().getTime(),(byte)1
);
hasReadSuccess = true;
}
if(!hasReadSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.AckParameterError))
);
}
//没有错误不回复消息
break;
}
case SetMsgSuccess: {
boolean setStatusSuccess = false;
SetSuccessStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetSuccessStatusMessage.class);
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Succeed);
setStatusSuccess = true;
}
if(!setStatusSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError))
);
}else{
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.Ok))
);
}
break;
}
case SetMsgReverted: {
boolean setStatusSuccess = false;
SetRevertedStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetRevertedStatusMessage.class);
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Reverted);
setStatusSuccess = true;
}
if(!setStatusSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError))
);
}else{
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.Ok))
);
}
break;
}
case SetMsgDeleted: {
boolean setStatusSuccess = false;
SetDeletedStatusMessage inSysData = JacksonUtil.jsonToBean(data, SetDeletedStatusMessage.class);
if(null != inSysData.getData() && StrUtil.isNotEmpty(inSysData.getData().getMsgId())){
updateMessageStatus(inSysData.getData().getMsgId(), MessageConstant.Status.Deleted);
setStatusSuccess = true;
}
if(!setStatusSuccess){
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.SetStatusParameterError))
);
}else{
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.Ok))
);
}
break;
}
case ClientIdleClosed: {
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(
MessageConstant.Error.ChannelIdle.joinExtra(String.valueOf(getSettingProps().getKeepAlive().getMaxIdleSeconds()))
)
)
);
break;
}
case UnExceptedError: {
UnExceptedErrorMessage inSysData = JacksonUtil.jsonToBean(data, UnExceptedErrorMessage.class);
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(
MessageConstant.Error.UnExpectedError.joinExtra(inSysData.getData().getErrorString())
)
)
);
break;
}
case ClientAuthTimeOut:{
outMessage = new OutMessage(JacksonUtil.beanToJson(
new ServerAckMessage(MessageConstant.Error.ChannelAuthTimeOut)
)
);
break;
}
default: break;
}
//2.结果应答
if(null != outMessage) {
ClientManager.sendServerMessage(inMessage.getFromDomain(),
OutMessageSet.newInstance().ackId(null).add(outMessage)
);
}
}
private static void onClientOnLine(MessageConstant.DomainType domain,String userId){
getMessageDao().updateMessageStatusToPending(domain,userId);
addClientToRedisUnPendingSet(CcMessageUtil.getDomainTypeAndUserIdString(domain,userId));
}
private static void updateMessageAckStatus(String ackId) throws Exception {
String ackKey = RedisKeyManager.getAckSetKey(ackId);
//1.从redis中获取ackId对应的set列表
if(!RedisUtil.hasKey(ackKey)) {
return;
}
//2.从redis中获取set集合,并删除ackId对应的set列表
Set<Object> ackMessageSet = RedisUtil.sGet(ackKey);
RedisUtil.del(ackKey);
//2.更新msg的ack标志
if (CollectionUtil.isNotEmpty(ackMessageSet)) {
for (Object msgId : ackMessageSet) {
Message message = getMessageDao().getMessageById((String) msgId);
if(message.getRule().getAckIsSuccess() == 0) {
getMessageDao().updateMessageAckAndStatus((String) msgId, (byte)1,MessageConstant.Status.TempSucceed);
}else{
getMessageDao().updateMessageAckAndStatus((String) msgId, (byte)1,MessageConstant.Status.Succeed);
}
}
}
//3.查找该用户是否仍然有未ack消息,
addClientToRedisUnPendingSet(ackId);
}
private static void updateMessageHasReadStatus(String fromDomain, String fromUserId,String toDomain, String toUserId,
Long time, byte hasRead) throws Exception{
getMessageDao().updateMessageReadStatus(fromDomain,fromUserId,toDomain,toUserId,time,hasRead);
}
private static void updateMessageStatus(String rawId, MessageConstant.Status status) throws Exception {
getMessageDao().updateMessageStatusByRawId(rawId,status);
if(status == MessageConstant.Status.Reverted){
List<Message> messageList = getMessageDao().getMessageByRawId(rawId);
if(CollectionUtil.isNotEmpty(messageList)){
//ClientManager.sendServerMessage();
//为了保证投递到目标客户端,将该消息添加到消息队列中,进行投递。
for(Message message : messageList) {
Message invertedMessage = new Message(
MessageConstant.DomainType.Server, "", message.getToDomain(),message.getTo(),
JacksonUtil.beanToJson(new InvertedMessage(message.getId())),MessageRule.defaultRule(MessageConstant.DomainType.Server)
);
getMessageDao().saveOrUpdateMessage(invertedMessage);
}
}
}
}
private static void addClientToRedisUnPendingSet(String domainTypeAndUserId){
//如果有未ack消息并且不在当前待处理列表中,则添加
String redisUnPendingClientSetKey = RedisKeyManager.getPendingClientSetKey();
String redisWaitAckSetKey = RedisKeyManager.getAckSetKey(domainTypeAndUserId);
String []stringArray = CcMessageUtil.splitTypeAndUserId(domainTypeAndUserId);
MessageConstant.DomainType toDomain = MessageConstant.DomainType.valueOf(stringArray[0]);
String to = stringArray[1];
//1.1 当前用户是否在pendingClients列表中
if (!RedisUtil.sHas(redisUnPendingClientSetKey,domainTypeAndUserId)) {
//1.2 当前用户是否在redis的等待ack列表中
if(!RedisUtil.hasKey(redisWaitAckSetKey)){
//1.3 当前用户是否有pending消息
if(getMessageDao().countClientPendingMessage(toDomain,to) > 0){
//1.4 将当前用户添加到redisUnPendingClientSet列表中排队处理
RedisUtil.sSet(redisUnPendingClientSetKey,domainTypeAndUserId);
}
}
}
}
}

31
src/main/java/com/ccsens/ccmq/lowlevel/message/client/AckMessage.java

@ -1,31 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class AckMessage extends ServerMessage {
@Setter
@Getter
public static class Data {
String ackId;
}
private Data data;
public AckMessage(){
setType(MessageConstant.ClientMessageType.Ack.name());
}
public AckMessage(String ackId){
this();
data = new Data();
data.setAckId(ackId);
}
}

36
src/main/java/com/ccsens/ccmq/lowlevel/message/client/AuthMessage.java

@ -1,36 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
* 客户端认证 (user)
*/
@Data
public class AuthMessage extends ServerMessage {
@Setter
@Getter
public static class Data{
private String token;
private int major;
private int minor;
}
private Data data;
public AuthMessage(){
setType(MessageConstant.ClientMessageType.Auth.name());
}
public AuthMessage(String token,int major,int minor){
this();
data = new Data();
data.setToken(token);
data.setMajor(major);
data.setMinor(minor);
}
}

16
src/main/java/com/ccsens/ccmq/lowlevel/message/client/ClientAuthTimeOutMessage.java

@ -1,16 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
/**
* @author wei
* 客户端向服务器请求当前连接状态
*/
@Data
public class ClientAuthTimeOutMessage extends ServerMessage {
public ClientAuthTimeOutMessage(){
setType(MessageConstant.ClientMessageType.ClientAuthTimeOut.name());
}
}

16
src/main/java/com/ccsens/ccmq/lowlevel/message/client/ClientIdleClosedMessage.java

@ -1,16 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
/**
* @author wei
* 客户端向服务器请求当前连接状态
*/
@Data
public class ClientIdleClosedMessage extends ServerMessage {
public ClientIdleClosedMessage(){
setType(MessageConstant.ClientMessageType.ClientIdleClosed.name());
}
}

16
src/main/java/com/ccsens/ccmq/lowlevel/message/client/GetStatusMessage.java

@ -1,16 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
/**
* @author wei
* 客户端向服务器请求当前连接状态
*/
@Data
public class GetStatusMessage extends ServerMessage {
public GetStatusMessage(){
setType(MessageConstant.ClientMessageType.GetChannelStatus.name());
}
}

36
src/main/java/com/ccsens/ccmq/lowlevel/message/client/HasReadMessage.java

@ -1,36 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class HasReadMessage extends ServerMessage {
/**
* fromDomain域的fromUserId用户 给我发送的所有 时间<lastMsgId时间的消息 都已读
*/
@Setter
@Getter
public static class Data {
private Long time;
private String fromUserId;
private String fromDomain;
}
private Data data;
public HasReadMessage(){
setType(MessageConstant.ClientMessageType.HasRead.name());
}
public HasReadMessage(Long time){
this();
data = new Data();
data.setTime(time);
}
}

32
src/main/java/com/ccsens/ccmq/lowlevel/message/client/PingMessage.java

@ -1,32 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class PingMessage extends ServerMessage {
@Setter
@Getter
public static class Data{
private Integer major;
private Integer minor;
}
private Data data;
public PingMessage(){
setType(MessageConstant.ClientMessageType.Ping.name());
}
public PingMessage(Integer major,Integer minor){
this();
data = new Data();
data.setMajor(major);
data.setMinor(minor);
}
}

31
src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetDeletedStatusMessage.java

@ -1,31 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class SetDeletedStatusMessage extends ServerMessage {
@Setter
@Getter
public static class Data {
String msgId;
}
private Data data;
public SetDeletedStatusMessage(){
setType(MessageConstant.ClientMessageType.SetMsgDeleted.name());
}
public SetDeletedStatusMessage(String msgId){
this();
data = new Data();
data.setMsgId(msgId);
}
}

31
src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetRevertedStatusMessage.java

@ -1,31 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class SetRevertedStatusMessage extends ServerMessage {
@Setter
@Getter
public static class Data {
String msgId;
}
private Data data;
public SetRevertedStatusMessage(){
setType(MessageConstant.ClientMessageType.SetMsgReverted.name());
}
public SetRevertedStatusMessage(String msgId){
this();
data = new Data();
data.setMsgId(msgId);
}
}

31
src/main/java/com/ccsens/ccmq/lowlevel/message/client/SetSuccessStatusMessage.java

@ -1,31 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
*/
@Data
public class SetSuccessStatusMessage extends ServerMessage {
@Setter
@Getter
public static class Data {
String msgId;
}
private Data data;
public SetSuccessStatusMessage(){
setType(MessageConstant.ClientMessageType.SetMsgSuccess.name());
}
public SetSuccessStatusMessage(String msgId){
this();
data = new Data();
data.setMsgId(msgId);
}
}

31
src/main/java/com/ccsens/ccmq/lowlevel/message/client/UnExceptedErrorMessage.java

@ -1,31 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.client;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author wei
* 客户端向服务器请求当前连接状态
*/
@Data
public class UnExceptedErrorMessage extends ServerMessage {
@Getter@Setter
public static class Data{
private String errorString;
}
private Data data;
public UnExceptedErrorMessage(){
setType(MessageConstant.ClientMessageType.UnExceptedError.name());
}
public UnExceptedErrorMessage(String errorString){
this();
data = new Data();
data.setErrorString(errorString);
}
}

95
src/main/java/com/ccsens/ccmq/lowlevel/message/common/InMessage.java

@ -1,95 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import cn.hutool.core.date.DateUtil;
import wiki.tall.ccmq.common.util.JacksonUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.Data;
import java.util.*;
/**
* @author wei
*/
@Data
public class InMessage {
/**
* 消息ID
*/
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String id;
/**
* 发送时间(s)
* Notice: 指的是服务器收到发送请求的时间不是服务器发出消息的时间
*/
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long time;
/**
* 消息来自于那个域
*/
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private MessageConstant.DomainType fromDomain;
/**
* 发送者信息
*/
private String from;
/**
* 消息要发送到哪个域
*/
private MessageConstant.DomainType toDomain;
/**
* 接受者信息列表
*/
private Set<String> tos;
/**
* 消息的标示符通常是由用户传过来的消息ID该消息在用户系统中的ID
*/
private String unikey;
/**
* 发送规则
* DeSerialize but not serialize @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
*/
private MessageRule rule;
/**
* 发送内容
* 如果toDomain是Server 代表消息是发给当前服务器的必须是MessageSysData的子类型
* 其他格式不限由用户指定
*/
private String data;
public InMessage(){
this.time = DateUtil.currentSeconds();
}
public static InMessage newToServerMessage(MessageConstant.DomainType fromDomain,ServerMessage serverMessage) throws JsonProcessingException {
InMessage inMessage = new InMessage();
inMessage.setFromDomain(fromDomain);
inMessage.setToDomain(MessageConstant.DomainType.Server);
inMessage.setData(JacksonUtil.beanToJson(serverMessage));
return inMessage;
}
public static InMessage newToQueueMessage(String from, Set<String> tos, String unikey, MessageRule rule, String data) throws JsonProcessingException {
InMessage inMessage = new InMessage();
inMessage.setToDomain(MessageConstant.DomainType.Queue);
inMessage.setFrom(from);
inMessage.setTos(tos);
inMessage.setUnikey(unikey);
inMessage.setRule(rule);
inMessage.setData(data);
return inMessage;
}
public static InMessage newToUserMessage(String from, Set<String> tos, String unikey, MessageRule rule, String data) throws JsonProcessingException {
InMessage inMessage = new InMessage();
inMessage.setToDomain(MessageConstant.DomainType.User);
inMessage.setFrom(from);
inMessage.setTos(tos);
inMessage.setUnikey(unikey);
inMessage.setRule(rule);
inMessage.setData(data);
return inMessage;
}
//TODO
//添加方便链式调用的构造方法,类似builder
}

134
src/main/java/com/ccsens/ccmq/lowlevel/message/common/Message.java

@ -1,134 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import wiki.tall.ccmq.common.util.DateUtil;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* @author wei
*/
@Data
public class Message {
/**
* 消息ID (Message's ID)
*/
private String id;
/**
* 原始消息ID (InMessage's ID)
*/
private String rawId;
/**
* 发送时间(s)
* Notice: 指的是服务器收到发送请求的时间不是服务器发出消息的时间
*/
private Long time;
/**
* 消息来自于那个域
*/
private MessageConstant.DomainType fromDomain;
/**
* 发送者信息
*/
private String from;
/**
* 消息要发送到哪个域
*/
private MessageConstant.DomainType toDomain;
/**
* 接受者信息列表
*/
private String to;
/**
* 发送规则
* DeSerialize but not serialize @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
*/
private MessageRule rule;
/**
* 发送内容
* 如果toDomain是Server 代表消息是发给当前服务器的必须是MessageSysData的子类型
* 其他格式不限由用户指定
*/
private String data;
/**
* 是否收到用户的ack消息
*/
private byte acked;
/**
* 是否收到用户的已读消息
*/
private byte hasRead;
/**
* 消息状态
* 发送成功标志根据配置由三种情况
* 1. 配置不需要客户端ack
* 发送完毕自动设置 ack = true;
* rule -> ackIsSuccess = true 发送即代表成功
* rule -> ackIsSuccess = false 等待用户手动设置成功标志
* 2. 配置需要ack则发送并受到ack代表成功
* rule -> ackIsSuccess = true 发送并收到ack即代表成功
* rule -> ackIsSuccess = false 等待用户手动设置成功标志
*/
private MessageConstant.Status status;
/**
* 发送次数
*/
private int sendTimes;
/**
* 发送时间
*/
private List<Long> sendTimeInSecondList;
public Message(){
}
public Message(MessageConstant.DomainType fromDomain,String from,MessageConstant.DomainType toDomain,String to,String data,MessageRule rule){
this.time = DateUtil.currentSeconds();
this.fromDomain = fromDomain;
this.from = from;
this.toDomain = toDomain;
this.to = to;
this.data = data;
this.rule = rule;
}
public Message(InMessage inEntity, String to){
this.rawId = inEntity.getId();
this.time = inEntity.getTime();
this.fromDomain = inEntity.getFromDomain();
this.from = inEntity.getFrom();
this.toDomain = inEntity.getToDomain();
this.to = to;
this.rule = inEntity.getRule();
this.data = inEntity.getData();
this.acked = 0;
this.hasRead = 0;
this.status = MessageConstant.Status.Pending;
this.sendTimes = 0;
}
public void incrementSendTimes() {
this.sendTimes++;
if(this.sendTimeInSecondList == null){
this.sendTimeInSecondList = new ArrayList<>();
}
this.sendTimeInSecondList.add(DateUtil.currentSeconds());
}
public boolean isSendTimesUpLimit() {
if((rule.getNoAckRetryTimes() > 0) && (sendTimes >= rule.getNoAckRetryTimes())){
return true;
}
return false;
}
public boolean isExpired() {
return DateUtil.currentSeconds() >= rule.getExpireAt();
}
}

217
src/main/java/com/ccsens/ccmq/lowlevel/message/common/MessageConstant.java

@ -1,217 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* @author wei
* 消息相关常量
*/
public class MessageConstant {
public enum ClientMessageType{
//客户端心跳
Ping(0x00),
//客户端认证
Auth(0x01),
//客户端收到消息ACK
Ack(0x02),
//客户端收到消息ACK
HasRead(0x03),
//客户端请求连接状态
GetChannelStatus(0x04),
//不可预期的错误
UnExceptedError(0x21),
ClientIdleClosed(0x22),
ClientAuthTimeOut(0x23),
//设置消息状态消息(disable撤销消息,适用于,消息撤回),针对消息本身(eg:消息撤回)
SetMsgSuccess(0x52),
SetMsgReverted(0x53),
SetMsgDeleted(0x54);
public int value;
ClientMessageType(int value){
this.value = value;
}
/**
* 从int到enum的转换函数
* @param value 枚举int值
* @return 对应的枚举找不到则返回null
*/
public static ClientMessageType valueOf(int value) {
for(ClientMessageType type : values()){
if(type.value == value){
return type;
}
}
return null;
}
}
public enum ServerMessageType{
//客户端心跳
Pong(0x00),
//客户端收到消息ACK
Ack(0x02),
//客户端请求连接状态
ChannelStatus(0x03);
//撤销某个消息
//DelMessage(0x04),
//客户端请求连接状态
//QueueStatus(0x05);
public int value;
ServerMessageType(int value){
this.value = value;
}
/**
* 从int到enum的转换函数
* @param value 枚举int值
* @return 对应的枚举找不到则返回null
*/
public static ServerMessageType valueOf(int value) {
for(ServerMessageType type : values()){
if(type.value == value){
return type;
}
}
return null;
}
}
/**
* JsonFormat是Json Serialize相关配置
*/
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Error{
//Ok
Ok(200,"Ok"),
/**
* 通用消息错误
*/
//消息无接收者错误
MessageNoReceiversError(1001,"[用户]消息至少应该有一个接收者"),
//消息无数据错误
MessageNoDataError(1002,"不允许消息内容为空"),
/**
* server消息错误
*/
//server消息无type错误
MessageNoTypeError(1003,"发送至Server域的消息必须有type字段"),
//Ack参数错误,没有找到对应的msgId
AckParameterError(1103,"Ack参数错误,没有找到对应的msgId"),
//SetSuccess参数错误,没有找到对应的msgId
SetSuccessParameterError(1104,"SetSuccess参数错误,没有找到对应的msgId"),
//SetStatusParameterError
SetStatusParameterError(1105,"SetStatus参数错误,没有找到对应的msgId"),
//HasRead参数错误,没有找到对应的msgId
HasReadParameterError(1106,"HasRead参数错误,没有找到对应的msgId"),
/**
* 身份认证错误
*/
//无权限
UnAuthed(1021, "未认证的用户"),
/**
* 业务错误
*/
//认证失败
AuthFailed(1301,"认证失败"),
//空闲断开连接(连续Ns没有收到数据)
ChannelIdle(1302,"连接断开:连续N秒没有从收到客户端收到任何数据"),
//认证超时断开连接
ChannelAuthTimeOut(1303,"连接断开:认证超时"),
//不可预期错误
UnExpectedError(1304,"不可预期异常");
public int code;
public String text;
public String extra;
Error(int code,String text) {
this.code = code;
this.text = text;
}
Error(int code,String text,String extra) {
this.code = code;
this.text = text;
this.extra = extra;
}
public Error joinExtra(String extra){
this.extra = extra;
return this;
}
}
/**
* (UserQueueRestServer)
*/
public enum DomainType{
//Netty Client
User(1),
//Queue Client
Queue(2),
//Rest Client
Rest(3),
//系统
Server(4);
public int value;
DomainType(int value){
this.value = value;
}
public static DomainType valueOf(int value) {
for(DomainType domainType : values()){
if(domainType.value == value){
return domainType;
}
}
return null;
}
}
public enum Status{
//未决状态(未完成)
Pending(0),
//发送成功(投递成功)
Succeed(1),
//发送失败(投递失败)
Failed(2),
//消息过期
Expired(3),
//消息被撤回
Reverted(4),
//消息被删除
Deleted(5),
//消息达到重试次数上限
SendTimesUpLimit(6),
//临时完成状态,当ackIsSuccess==0时,一旦ack设置消息为此状态
TempSucceed(10);
public int value;
Status(int value){
this.value = value;
}
public static Status valueOf(int value){
for(Status status : values()){
if(status.value == value){
return status;
}
}
return null;
}
}
}

80
src/main/java/com/ccsens/ccmq/lowlevel/message/common/MessageRule.java

@ -1,80 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import lombok.Data;
/**
* 发送规则实体类
* @author wei
*/
@Data
public class MessageRule {
public enum AckRule{
//不需要ack
NONE,
//产生错误时回复ack
ERROR,
//总是回复ack
ALWAYS
}
/**
* 离线丢弃标志
*/
private byte offlineDiscard;
/**
* 消息是否需要ack如果不需要ack则发送完毕即代表成功
*/
private AckRule ackRule;
/**
* 没有收到ack重试次数
*/
private Integer noAckRetryTimes;
/**
* 收到ack代表发送成功不再发送
* 如果该标志为0则代表每次上线都发送直到用户手动设置成功
*/
private byte ackIsSuccess;
/**
* 消息过期时间(s)
*/
private Long expireAt;
public MessageRule(){
}
public MessageRule(byte offlineDiscard,AckRule ackRule,Integer noAckRetryTimes,byte ackIsSuccess){
this.offlineDiscard = offlineDiscard;
this.ackRule = ackRule;
this.noAckRetryTimes = noAckRetryTimes;
this.ackIsSuccess = ackIsSuccess;
}
public MessageRule(byte offlineDiscard,AckRule ackRule,Integer noAckRetryTimes,byte ackIsSuccess,Long expireAt){
this.offlineDiscard = offlineDiscard;
this.ackRule = ackRule;
this.noAckRetryTimes = noAckRetryTimes;
this.ackIsSuccess = ackIsSuccess;
this.expireAt = expireAt;
}
public static MessageRule defaultRule(MessageConstant.DomainType fromDomain) {
MessageRule messageRule = null;
switch(fromDomain) {
case User:
messageRule = new MessageRule((byte) 0, AckRule.ALWAYS, 10, (byte) 1,0L);
break;
case Queue:
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L);
break;
case Rest:
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L);
break;
case Server:
messageRule = new MessageRule((byte) 1, AckRule.ALWAYS, 10, (byte) 1,0L);
break;
default:
break;
}
return messageRule;
}
}

71
src/main/java/com/ccsens/ccmq/lowlevel/message/common/OutMessage.java

@ -1,71 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
/**
* @author wei
*/
@Data
public class OutMessage {
/**
* 消息ID
*/
private String id;
/**
* sender发送消息时间(s)
* Notice: 指的是服务器收到发送请求的时间不是服务器发出消息的时间服务器发出消息的时间客户端很好确定就是当前时间
*/
private Long time;
/**
* 消息来自于那个域通常不需要知道toDomain
*/
private MessageConstant.DomainType fromDomain;
/**
* 发送者信息
*/
private String from;
/**
* 如果toDomain是Server 代表消息是发给当前服务器的必须是MessageSysData的子类型
* 其他格式不限由用户指定消息系统会将data字段原封不动投递到接收方由接收方进行解析
*/
private String data;
public OutMessage(){
}
public OutMessage(String id, Long time, String from, String data){
this.id = id;
this.time = time;
this.from = from;
this.data = data;
}
public OutMessage(InMessage messageEntity){
this.id = messageEntity.getId();
this.time = messageEntity.getTime();
this.fromDomain = messageEntity.getFromDomain();
this.from = messageEntity.getFrom();
this.data = messageEntity.getData();
}
public OutMessage(String data){
this.time = DateUtil.currentSeconds();
this.fromDomain = MessageConstant.DomainType.Server;
this.data = data;
}
public OutMessage(MessageConstant.DomainType fromDomain, String data) {
this.time = DateUtil.currentSeconds();
this.fromDomain = fromDomain;
this.data = data;
}
public OutMessage(Message message){
this.id = message.getId();
this.time = message.getTime();
this.fromDomain = message.getFromDomain();
this.from = message.getFrom();
this.data = message.getData();
}
}

43
src/main/java/com/ccsens/ccmq/lowlevel/message/common/OutMessageSet.java

@ -1,43 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author __zHangSan
*/
@Getter @Setter @ToString
public class OutMessageSet {
private String ackId;
private Set<OutMessage> messageSet;
public OutMessageSet ackId(String ackId){
this.ackId = ackId;
return this;
}
public static OutMessageSet newInstance(){
return new OutMessageSet();
}
public OutMessageSet add(OutMessage messageOutEntity){
if(messageSet == null){
messageSet = new HashSet<>();
}
messageSet.add(messageOutEntity);
return this;
}
public OutMessageSet addAll(Collection<OutMessage> theMessageSet){
if(messageSet == null){
messageSet = new HashSet<>();
}
messageSet.addAll(theMessageSet);
return this;
}
}

11
src/main/java/com/ccsens/ccmq/lowlevel/message/common/ServerMessage.java

@ -1,11 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.common;
import lombok.Data;
/**
* @author wei
*/
@Data
public class ServerMessage {
private String type;
}

37
src/main/java/com/ccsens/ccmq/lowlevel/message/server/ChannelStatusMessage.java

@ -1,37 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.server;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author zhangsan
*/
@Data
public class ChannelStatusMessage extends ServerMessage {
@Setter
@Getter
public static class Data{
//认证状态
private Boolean authed;
//已认证时长(s)
private Long onlineSeconds;
//错误信息
private MessageConstant.Error error;
}
private Data data;
public ChannelStatusMessage(){
setType(MessageConstant.ServerMessageType.ChannelStatus.name());
}
public ChannelStatusMessage(Boolean authed,Long onlineSeconds,MessageConstant.Error error){
this();
data = new Data();
data.authed = authed;
data.onlineSeconds = onlineSeconds;
data.error = error;
}
}

28
src/main/java/com/ccsens/ccmq/lowlevel/message/server/InvertedMessage.java

@ -1,28 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.server;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Getter;
import lombok.Setter;
/**
* @author __zHangSan
*/
public class InvertedMessage extends ServerMessage {
@Setter
@Getter
public static class Data{
private String msgId;
}
private Data data;
public InvertedMessage(){
setType(MessageConstant.ServerMessageType.Pong.name());
}
public InvertedMessage(String msgId){
this();
data = new Data();
data.setMsgId(msgId);
}
}

15
src/main/java/com/ccsens/ccmq/lowlevel/message/server/PongMessage.java

@ -1,15 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.server;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
/**
* @author zhangsan
*/
@Data
public class PongMessage extends ServerMessage {
public PongMessage(){
setType(MessageConstant.ServerMessageType.Pong.name());
}
}

30
src/main/java/com/ccsens/ccmq/lowlevel/message/server/QueueStatusMessage.java

@ -1,30 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.server;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author zhangsan
*/
@Data
public class QueueStatusMessage extends ServerMessage {
@Setter
@Getter
public static class Data{
private MessageConstant.Error error;
}
private Data data;
public QueueStatusMessage(){
setType(MessageConstant.ServerMessageType.ChannelStatus.name());
}
public QueueStatusMessage(MessageConstant.Error error){
this();
data = new Data();
data.error = error;
}
}

49
src/main/java/com/ccsens/ccmq/lowlevel/message/server/ServerAckMessage.java

@ -1,49 +0,0 @@
package com.ccsens.ccmq.lowlevel.message.server;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.ccsens.ccmq.lowlevel.message.common.ServerMessage;
import lombok.Data;
/**
* @author zhangsan
*/
@Data
public class ServerAckMessage extends ServerMessage {
@lombok.Data
public static class Data{
/**
* 消息ID
*/
private String msgId;
/**
* 发送者指定的unikey如果发送者未指定则为null
*/
private String unikey;
/**
* 消息状态
*/
private MessageConstant.Error error;
public Data(String msgId,String unikey,MessageConstant.Error error){
this.msgId = msgId;
this.unikey = unikey;
this.error = error;
}
}
private Data data;
public ServerAckMessage(){
setType(MessageConstant.ServerMessageType.Ack.name());
}
public ServerAckMessage(MessageConstant.Error status){
this();
this.data = new Data(null,null,status);
}
public ServerAckMessage(String msgId,String unikey,MessageConstant.Error status){
this();
this.data = new Data(msgId,unikey,status);
}
}

118
src/main/java/com/ccsens/ccmq/lowlevel/persist/IMessageDao.java

@ -1,118 +0,0 @@
package com.ccsens.ccmq.lowlevel.persist;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.Message;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import java.util.List;
/**
* @author wei
*/
public interface IMessageDao {
/**
* 保存原始消息
* @param inMessage message to be saved
* @throws Exception 可能产生的异常
*/
void saveOrUpdateMessage(InMessage inMessage) throws Exception;
/**
* 保存message
* @param message message to be saved
* @throws Exception 可能产生的异常
*/
void saveOrUpdateMessage(Message message) throws Exception;
/**
* 更新消息的某个接受者的hasRead状态
* @param fromDomain 消息fromDomain
* @param fromUserId 消息from
* @param toDomain 消息toDomain
* @param toUserId 接受者ID
* @param time 该time之前包括自己的消息
* @param hasRead 是否已读
* @throws Exception
*/
void updateMessageReadStatus(String fromDomain, String fromUserId, String toDomain, String toUserId, Long time, byte hasRead)throws Exception;
/**
* 更新消息的某个接受者的 ack和success状态
* @param msgId 消息ID
* @param acked 是否ack
* @param status status
*/
void updateMessageAckAndStatus(String msgId, byte acked, MessageConstant.Status status);
/**
* 根据rawId修改message status
* @param rawId 原始消息ID
* @param status 要修改的状态
*/
void updateMessageStatusByRawId(String rawId, MessageConstant.Status status);
/**
* 更新消息状态
* @param msgId
* @param status NormalDeleted
*/
void updateMessageStatus(String msgId, MessageConstant.Status status);
/**
* 更新所有的status==TempSucceed的消息为pending
*/
void updateMessageStatusToPending();
/**
* 更新所有的status==TempSucceed的消息为pending
* @param domainType
* @param userId 用户
*/
void updateMessageStatusToPending(MessageConstant.DomainType domainType, String userId);
/**
* 检查pending消息是否过期如果是设置为expired状态
*/
void updateMessageToExpired();
/**
* 检查pending消息是否达到发送上限如果是设置为SendTimesUpLimit状态
*/
void updateMessageToSendTimesUpLimit();
/**
* 增加某个消息的发送次数并添加一条发送记录
* @param id 消息ID
* @param time 发送时间
*/
void incrementSendTimes(String id, Long time);
/**
* 根据ID获取MessageEntity
* @param msgId 消息ID
* @return 消息实体类
*/
Message getMessageById(String msgId);
/**
* 根据ID获取MessageEntity
* @param rawId 消息ID
* @return 消息实体类集合
*/
List<Message> getMessageByRawId(String rawId);
/**
* 获取某个客户端pending的消息集合最多获取maxMessageNum个
* @param toDomain 消息的接收域
* @param to 消息的接收者Id
* @param maxMessageNum 最多查询数量
* @return 消息集合
*/
List<Message> getClientPendingMessage(MessageConstant.DomainType toDomain, String to, Integer maxMessageNum);
/**
* 获取toDomain域to用户的未ack消息数量
* @param toDomain
* @param to 用户
* @return 未ack消息数量
*/
Long countClientPendingMessage(MessageConstant.DomainType toDomain, String to);
}

180
src/main/java/com/ccsens/ccmq/lowlevel/persist/MessageDao.java

@ -1,180 +0,0 @@
package com.ccsens.ccmq.lowlevel.persist;
import com.ccsens.ccmq.lowlevel.message.common.InMessage;
import com.ccsens.ccmq.lowlevel.message.common.Message;
import com.ccsens.ccmq.lowlevel.message.common.MessageConstant;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import wiki.tall.ccmq.common.util.DateUtil;
import java.util.List;
/**
* @author wei
*/
@Repository
public class MessageDao implements IMessageDao {
private static final String COLLECTION_RAW_MESSAGE = "col_message_raw";
private static final String COLLECTION_MESSAGE = "col_message";
@Autowired
private MongoTemplate mongoTemplate;
@Override
public void saveOrUpdateMessage(InMessage inMessage) throws Exception {
mongoTemplate.save(inMessage,COLLECTION_RAW_MESSAGE);
}
@Override
public void saveOrUpdateMessage(Message message) throws Exception {
mongoTemplate.save(message,COLLECTION_MESSAGE);
}
@Override
public void updateMessageReadStatus(String fromDomain,String fromUserId,String toDomain,String toUserId,Long time,byte hasRead) throws Exception {
Query query = new Query(
Criteria.where("fromDomain").is(fromDomain).andOperator(
Criteria.where("from").is(fromUserId).andOperator(
Criteria.where("toDomain").is(toDomain).andOperator(
Criteria.where("to").is(toUserId).andOperator(
Criteria.where("time").lte(time)
)
)
))
);
Update update = new Update().set("hasRead",hasRead);
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageAckAndStatus(String msgId, byte acked, MessageConstant.Status status) {
Query query = new Query(Criteria.where("id").is(msgId));
Update update = new Update().set("acked",acked).set("status",status);
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageStatusByRawId(String rawId, MessageConstant.Status status) {
Query query = new Query(Criteria.where("rawId").is(rawId));
Update update = new Update().set("status",status);
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageStatus(String msgId, MessageConstant.Status status) {
Query query = new Query(Criteria.where("id").is(msgId));
Update update = new Update().set("status",status);
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageStatusToPending() {
Query query = new Query(Criteria.where("status").is(MessageConstant.Status.TempSucceed));
Update update = new Update().set("status",MessageConstant.Status.Pending);
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageStatusToPending(MessageConstant.DomainType domainType, String userId) {
Query query = new Query()
.addCriteria(Criteria.where("toDomain").is(domainType))
.addCriteria(Criteria.where("to").is(userId))
.addCriteria(Criteria.where("status").is(MessageConstant.Status.TempSucceed));
Update update = new Update().set("status",MessageConstant.Status.Pending);
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageToExpired() {
Query query = new Query()
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending))
.addCriteria(Criteria.where("rule.expireAt").gt(0).lt(DateUtil.currentSeconds()));
Update update = new Update().set("status",MessageConstant.Status.Expired);
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void updateMessageToSendTimesUpLimit() {
Query query = new Query()
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending))
.addCriteria(Criteria.where("rule.noAckRetryTimes").gt(0))
.addCriteria(new Criteria(){
@Override
public Document getCriteriaObject() {
Document obj = new Document();
obj.put("$where", "this.sendTimes >= this.rule.noAckRetryTimes");
return obj;
}
});
Update update = new Update().set("status",MessageConstant.Status.SendTimesUpLimit);
mongoTemplate.updateMulti(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public void incrementSendTimes(String msgId,Long time) {
Query query = new Query(Criteria.where("id").is(msgId));
Update update = new Update();
update.inc("sendTimes");
update.addToSet("sendTimeInSecondList", time);
mongoTemplate.updateFirst(query,update, Message.class,COLLECTION_MESSAGE);
}
@Override
public Message getMessageById(String msgId) {
return mongoTemplate.findById(msgId, Message.class,COLLECTION_MESSAGE);
}
@Override
public List<Message> getMessageByRawId(String rawId) {
Query query = new Query().addCriteria(Criteria.where("rawId").is(rawId));
return mongoTemplate.find(query,Message.class);
}
@Override
public List<Message> getClientPendingMessage(MessageConstant.DomainType toDomain, String to, Integer maxMessageNum) {
Query query = clientPendingMessageQuery(toDomain,to)
.with(Sort.by(Sort.Order.asc("time")));
if(maxMessageNum == null || maxMessageNum.intValue() == 0) {
query.limit(maxMessageNum);
}
return mongoTemplate.find(query,Message.class,COLLECTION_MESSAGE);
}
@Override
public Long countClientPendingMessage(MessageConstant.DomainType toDomain, String to) {
Query query = clientPendingMessageQuery(toDomain,to);
return mongoTemplate.count(query,Message.class,COLLECTION_MESSAGE);
}
private Query clientPendingMessageQuery(MessageConstant.DomainType toDomain, String to){
return new Query().addCriteria(Criteria.where("toDomain").is(toDomain))
.addCriteria(Criteria.where("to").is(to))
//状态为pending
.addCriteria(Criteria.where("status").is(MessageConstant.Status.Pending))
.addCriteria(new Criteria().andOperator(
//未过期消息
new Criteria().orOperator(
Criteria.where("rule.expireAt").is(0),
Criteria.where("rule.expireAt").gt(DateUtil.currentSeconds()
)),
//未达到发送次数上限
new Criteria().orOperator(
Criteria.where("rule.noAckRetryTimes").is(0),
new Criteria() {
@Override
public Document getCriteriaObject() {
Document obj = new Document();
obj.put("$where", "this.sendTimes < this.rule.noAckRetryTimes");
return obj;
}
})
));
}
}

9
src/main/java/com/ccsens/ccmq/lowlevel/service/IUserService.java

@ -1,9 +0,0 @@
package com.ccsens.ccmq.lowlevel.service;
import org.springframework.web.bind.annotation.RequestParam;
//@FeignClient(value = "eureka-client-btpro",fallback = TokenSer.class)
public interface IUserService {
//@GetMapping(value="/btpro/v1.0/token")
String getUserIdByToken(@RequestParam(value = "token") String token);
}

15
src/main/java/com/ccsens/ccmq/lowlevel/service/UserService.java

@ -1,15 +0,0 @@
package com.ccsens.ccmq.lowlevel.service;
import org.springframework.stereotype.Service;
/**
* @author wei
*/
@Service
public class UserService implements IUserService{
@Override
public String getUserIdByToken(String token) {
String userId = "1122331122233";
return userId;
}
}

36
src/main/java/com/ccsens/opensource/wxconfigurer/WxConfigurerApplication.java

@ -0,0 +1,36 @@
package com.ccsens.opensource.wxconfigurer;
import com.ccsens.opensource.wxconfigurer.util.WebConstant;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.io.File;
/**
* @author wei
*/
@ComponentScan(basePackages = {"com.ccsens.opensource.wxconfigurer.*"})
@MapperScan(basePackages = {"com.ccsens.opensource.wxconfigurer.*"})
@ServletComponentScan
@EnableScheduling
@EnableAsync
@SpringBootApplication
public class WxConfigurerApplication implements CommandLineRunner {
public static void main(String[] args) {
createNecessaryDir();
SpringApplication.run(WxConfigurerApplication.class, args);
}
private static void createNecessaryDir(){
}
@Override
public void run(String... args) throws Exception {
}
}

151
src/main/java/com/ccsens/opensource/wxconfigurer/bean/dto/WxGzhAction.java

@ -0,0 +1,151 @@
package com.ccsens.opensource.wxconfigurer.bean.dto;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.Data;
import java.util.List;
/**
* @author __zHangSan
*/
@Data
@JacksonXmlRootElement(localName = "xml")
public class WxGzhAction {
@Data
private static class ScanCodeInfo{
@JacksonXmlProperty(localName = "ScanType")
private String scanType;
@JacksonXmlProperty(localName = "ScanResult")
private String scanResult;
};
@Data
private static class SendPicsInfo{
@Data
private static class Item{
@JacksonXmlProperty(localName = "PicMd5Sum")
private String picMd5Sum;
}
@JacksonXmlProperty(localName = "Count")
private Integer count;
@JacksonXmlProperty(localName = "PicList")
private List<Item> picList;
}
@Data
private static class SendLocationInfo{
@JacksonXmlProperty(localName = "Location_X")
private String x;
@JacksonXmlProperty(localName = "Location_Y")
private String y;
@JacksonXmlProperty(localName = "Scale")
private String scale;
@JacksonXmlProperty(localName = "Label")
private String label;
@JacksonXmlProperty(localName = "Poiname")
private String poiname;
}
@JacksonXmlProperty(localName = "FromUserName")
private String openId;
@JacksonXmlProperty(localName = "ToUserName")
private String gzhId;
@JacksonXmlProperty(localName = "CreateTime")
private Long createTime;
@JacksonXmlProperty(localName = "MsgType")
private String msgType;
/**
* MsgType: Text (Content,MsgId)
*/
@JacksonXmlProperty(localName = "Content")
private String content;
@JacksonXmlProperty(localName = "MsgId")
private String msgId;
/**
* MsgType: image (PicUrl,MediaId,MsgId)
*/
@JacksonXmlProperty(localName = "PicUrl")
private String picUrl;
@JacksonXmlProperty(localName = "MediaId")
private String mediaId;
/**
* MsgType: voice (MediaId,Format,MsgId)
* Recognition: 开通语音识别后生效
*/
@JacksonXmlProperty(localName = "Format")
private String format;
@JacksonXmlProperty(localName = "Recognition")
private String recognition;
/**
* MsgType: video/shortvideo(MediaId,ThumbMediaId,MsgId)
*/
@JacksonXmlProperty(localName = "ThumbMediaId")
private String thumbMediaId;
/**
* MsgType: location(Location_x,Location_y,Scale,Label,MsgId)
*/
@JacksonXmlProperty(localName = "Location_X")
private String x;
@JacksonXmlProperty(localName = "Location_Y")
private String y;
@JacksonXmlProperty(localName = "Scale")
private String scale;
@JacksonXmlProperty(localName = "Label")
private String label;
/**
* MsgType: link(Title,Description,Url,MsgId)
*/
@JacksonXmlProperty(localName = "Title")
private String title;
@JacksonXmlProperty(localName = "Description")
private String description;
@JacksonXmlProperty(localName = "Url")
private String url;
/**
* MsgType: event
*/
@JacksonXmlProperty(localName = "Event")
private String event;
@JacksonXmlProperty(localName = "EventKey")
private String eventKey;
/**
* Event: subscribe/SCAN
*/
@JacksonXmlProperty(localName = "Ticket")
private String ticket;
/**
* Event: Location
*/
@JacksonXmlProperty(localName = "Latitude")
private String latitude;
@JacksonXmlProperty(localName = "Longitude")
private String longitude;
@JacksonXmlProperty(localName = "Precision")
private String precision;
/**
* Event: VIEW/view_miniprogram
*/
@JacksonXmlProperty(localName = "MenuId")
private String menuId;
/**
* Event: scancode_push/scancode_waitmsg
*/
@JacksonXmlProperty(localName = "ScanCodeInfo")
private ScanCodeInfo scanCodeInfo;
/**
* Event: pic_sysphoto/pic_photo_or_album/pic_weixin
*/
@JacksonXmlProperty(localName = "SendPicsInfo")
private SendPicsInfo sendPicsInfo;
/**
* Event: location_select
*/
@JacksonXmlProperty(localName = "SendLocationInfo")
private SendLocationInfo sendLocationInfo;
}

15
src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxAccessToken.java

@ -0,0 +1,15 @@
package com.ccsens.opensource.wxconfigurer.bean.po;
import lombok.Data;
/**
* @author __zHangSan
*/
@Data
public class WxAccessToken {
private String access_token;
private Long expires_in;
private Integer errcode;
private String errmsg;
private Long createdAt;
}

96
src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMenu.java

@ -0,0 +1,96 @@
package com.ccsens.opensource.wxconfigurer.bean.po;
import cn.hutool.core.collection.CollectionUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import java.util.Collection;
import java.util.List;
/**
* @author __zHangSan
*/
@Data
public class WxGzhMenu {
public enum ButtonType{
//点击推事件
CLICK("click"),
//跳转URL
VIEW("view"),
//小程序
MINIPROGRAM("miniprogram"),
//扫码推事件
SCANCODE_PUSH("scancode_push"),
//扫码推事件且弹出“消息接收中”提示框
SCANCODE_WAITMSG("scancode_waitmsg"),
//弹出系统拍照发图
PIC_SYSPHOTO("pic_sysphoto"),
//弹出拍照或者相册发图
PIC_PHOTO_OR_ALBUM("pic_photo_or_album"),
//弹出微信相册发图器
PIC_WEIXIN("pic_weixin"),
//弹出地理位置选择器
LOCATION_SELECT("location_select"),
//下发消息(除文本消息)
MEDIA_ID("media_id"),
//跳转图文消息URL
VIEW_LIMITED("view_limited");
@JsonValue
private String text;
ButtonType(String text){
this.text = text;
}
}
@Builder(toBuilder = true)
@Getter
public static class Button{
/**
* 必须
*/
private ButtonType type;
/**
* 必须
*/
private String name;
/**
* click等点击类型必须
*/
private String key;
/**
* viewminiprogram类型必须
*/
private String url;
/**
* media_id类型和view_limited类型必须
*/
private String media_id;
/**
* miniprogram类型必须
*/
private String appid;
private String pagepath;
/**
* optional
*/
private List<Button> sub_button;
}
@JsonProperty("button")
private List<Button> buttons;
public static WxGzhMenu newInstance(){
return new WxGzhMenu();
}
public WxGzhMenu add(Button button){
if(buttons == null){
buttons = CollectionUtil.newArrayList();
}
buttons.add(button);
return this;
}
}

52
src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMsgEvent.java

@ -0,0 +1,52 @@
package com.ccsens.opensource.wxconfigurer.bean.po;
import lombok.Getter;
import lombok.Setter;
/**
* @author __zHangSan
*/
public enum WxGzhMsgEvent {
/**
* 关注/取消关注事件
*/
SUBSCRIBE("subscribe"),UNSUBSCRIBE("unsubscribe"),
/**
* 扫描带参数公众号二维码事件
* 未关注产生Event: SUBSCRIBE EventKey: qrscene_xxx
* 已关注产生Event: Scan EventKey: scene_value
*/
SCAN("SCAN"),
/**
* 上报地理位置事件
*/
LOCATION("LOCATION"),
/**
* 自定义菜单事件
*/
//点击菜单拉取消息时的事件推送
CLICK("CLICK"),
//点击菜单跳转链接时的事件推送
VIEW("VIEW"),
//扫码推事件的事件推送
SCANCODE_PUSH("scancode_push"),
//扫码推事件且弹出“消息接收中”提示框的事件推送
SCANCODE_WAITMSG("scancode_waitmsg"),
//弹出系统拍照发图的事件推送
PIC_SYSPHOTO("pic_sysphoto"),
//弹出拍照或者相册发图的事件推送
PIC_PHOTO_OR_ALBUM("pic_photo_or_album"),
//弹出微信相册发图器的事件推送
PIC_WEIXIN("pic_weixin"),
//弹出地理位置选择器的事件推送
LOCATION_SELECT("location_select"),
//点击菜单跳转小程序的事件推送
VIEW_MINIPROGRAM("view_miniprogram");
@Getter@Setter
private String text;
private WxGzhMsgEvent(String text){
this.text = text;
}
}

33
src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxGzhMsgType.java

@ -0,0 +1,33 @@
package com.ccsens.opensource.wxconfigurer.bean.po;
import lombok.Getter;
import lombok.Setter;
/**
* @author __zHangSan
*/
public enum WxGzhMsgType {
/**
* 1.普通消息
*/
//1.1 文本消息
TEXT("text"),
IMAGE("image"),
VOICE("voice"),
VIDEO("video"),
SHORTVIDEO("shortvideo"),
LOCATION("location"),
LINK("link"),
/**
* 2.事件推送
*/
EVENT("event");
@Getter @Setter
private String text;
private WxGzhMsgType(String text){
this.text = text;
}
}

21
src/main/java/com/ccsens/opensource/wxconfigurer/bean/po/WxOperation.java

@ -0,0 +1,21 @@
package com.ccsens.opensource.wxconfigurer.bean.po;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* @author __zHangSan
*/
public enum WxOperation {
/**
* 创建公众号底部菜单
*/
CREATE_MENU("CreateMenu");
@JsonValue
private String text;
WxOperation(String text){
this.text = text;
}
}

14
src/main/java/wiki/tall/ccmq/common/config/DruidProps.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/DruidProps.java

@ -1,11 +1,12 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.alibaba.druid.pool.DruidDataSource;
import wiki.tall.ccmq.common.util.WebConstant;
import com.ccsens.opensource.wxconfigurer.util.WebConstant;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
@ -69,9 +70,12 @@ public class DruidProps {
}
public String getPassword(){
String key = System.getenv("CCSENS_TALL_TEST");
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, Base64.decode(key));
return aes.decryptStr(password, CharsetUtil.CHARSET_UTF_8);
if(StrUtil.isNotEmpty(password)) {
String key = System.getenv("CCSENS_TALL_TEST");
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, Base64.decode(key));
password = aes.decryptStr(password, CharsetUtil.CHARSET_UTF_8);
}
return password;
}
private DataSource createDruidDataSource(String url){

2
src/main/java/wiki/tall/ccmq/common/config/EnjoyConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/EnjoyConfig.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import com.jfinal.template.Engine;
import com.jfinal.template.ext.spring.JFinalViewResolver;

6
src/main/java/wiki/tall/ccmq/common/config/ExcepHandler.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/ExcepHandler.java

@ -1,7 +1,7 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import wiki.tall.ccmq.common.exception.BaseException;
import wiki.tall.ccmq.common.util.JsonResponse;
import com.ccsens.opensource.wxconfigurer.exception.BaseException;
import com.ccsens.opensource.wxconfigurer.util.JsonResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;

2
src/main/java/wiki/tall/ccmq/common/config/RabbitMqConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/RabbitMqConfig.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;

2
src/main/java/wiki/tall/ccmq/common/config/RedisConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/RedisConfig.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;

4
src/main/java/wiki/tall/ccmq/common/config/RestClientConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/RestClientConfig.java

@ -1,6 +1,6 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import wiki.tall.ccmq.common.util.HttpsClientRequestFactory;
import com.ccsens.opensource.wxconfigurer.util.HttpsClientRequestFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.web.client.RestTemplateBuilder;

2
src/main/java/wiki/tall/ccmq/common/config/ServletConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/ServletConfig.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

21
src/main/java/wiki/tall/ccmq/common/config/SettingProps.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/SettingProps.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import lombok.Getter;
import lombok.Setter;
@ -56,5 +56,24 @@ public class SettingProps {
private String nettyTcpHexPort;
@Value("${setting.netty.tcphex.type}")
private String nettyTcpHexType;
@Value("${setting.swagger.apiTitle}")
private String swaggerApiTitle;
@Value("${setting.swagger.apiDescription}")
private String swaggerApiDescription;
@Value("${setting.swagger.apiTermsOfServiceUrl}")
private String swaggerApiTermOfService;
@Value("${setting.swagger.apiVersion}")
private String swaggerApiVersion;
@Value("${setting.swagger.apiLicense}")
private String swaggerApiLicense;
@Value("${setting.swagger.apiLicenseUrl}")
private String swaggerApiLicenseUrl;
@Value("${setting.swagger.contact.name}")
private String swaggerContactName;
@Value("${setting.swagger.contact.site}")
private String swaggerContactSite;
@Value("${setting.swagger.contact.email}")
private String swaggerContactEmail;
}

4
src/main/java/wiki/tall/ccmq/common/config/SpringConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/SpringConfig.java

@ -1,9 +1,9 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import wiki.tall.ccmq.common.controller.interceptor.TokenInterceptor;
import com.ccsens.opensource.wxconfigurer.web.interceptor.TokenInterceptor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

40
src/main/java/wiki/tall/ccmq/common/config/SwaggerConfigure.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/SwaggerConfig.java

@ -1,8 +1,10 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import wiki.tall.ccmq.common.util.WebConstant;
import com.ccsens.opensource.wxconfigurer.util.WebConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
@ -21,35 +23,31 @@ import java.util.List;
*/
@Configuration
@EnableSwagger2
public class SwaggerConfigure {
public class SwaggerConfig {
@Autowired
private SettingProps settingProps;
@Bean
public Docket customDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors
.basePackage("com.ccsens.ccmq"))
.basePackage("com.ccsens.opensource"))
.build()
.globalOperationParameters(setHeaderToken());
}
private ApiInfo apiInfo() {
Contact contact = new Contact("__zHangSan", "", "weizezhao@gamil.com");
return new ApiInfo(
//大标题 title
"Anyring Core v3.0 Plugin Version.",
//小标题
"",
//版本
"1.0.0",
//termsOfServiceUrl
"",
//作者
contact.getName(),
//链接显示文字
"",
//网站链接
"www.ccsens.com"
);
return new ApiInfoBuilder().title(settingProps.getSwaggerApiTitle())
.description(settingProps.getSwaggerApiDescription())
.termsOfServiceUrl(settingProps.getSwaggerApiTermOfService())
.version(settingProps.getSwaggerApiVersion())
.license(settingProps.getSwaggerApiLicense())
.licenseUrl(settingProps.getSwaggerApiLicenseUrl())
.contact(new Contact(settingProps.getSwaggerContactName(),
settingProps.getSwaggerContactSite(),
settingProps.getSwaggerContactEmail())
).build();
}
private List<Parameter> setHeaderToken() {

2
src/main/java/wiki/tall/ccmq/common/config/TaskExecutorConfig.java → src/main/java/com/ccsens/opensource/wxconfigurer/config/TaskExecutorConfig.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.config;
package com.ccsens.opensource.wxconfigurer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

21
src/main/java/com/ccsens/opensource/wxconfigurer/exception/BaseException.java

@ -0,0 +1,21 @@
package com.ccsens.opensource.wxconfigurer.exception;
import lombok.Data;
/**
* @author __zHangSan
*/
@Data
public class BaseException extends Exception {
private String operation;
private Integer code;
public BaseException(){
}
public BaseException(Integer code,String message){
super(message);
this.code = code;
}
}

18
src/main/java/com/ccsens/opensource/wxconfigurer/exception/BusinessException.java

@ -0,0 +1,18 @@
package com.ccsens.opensource.wxconfigurer.exception;
import com.ccsens.opensource.wxconfigurer.bean.po.WxOperation;
/**
* @author __zHangSan
*/
public class BusinessException extends BaseException {
private String type = "BusinessError";
public BusinessException(){
}
public BusinessException(Integer errcode,String errmsg){
super(errcode,errmsg);
}
}

22
src/main/java/com/ccsens/opensource/wxconfigurer/exception/WxException.java

@ -0,0 +1,22 @@
package com.ccsens.opensource.wxconfigurer.exception;
import com.ccsens.opensource.wxconfigurer.bean.po.WxOperation;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
/**
* @author __zHangSan
*/
@Data
public class WxException extends BaseException{
private String type = "BusinessError";
public WxException(){
}
public WxException(Integer errcode,String errmsg){
super(errcode,errmsg);
}
}

2
src/main/java/wiki/tall/ccmq/common/util/BeanWrapperUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/BeanWrapperUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;

2
src/main/java/wiki/tall/ccmq/common/util/CRCUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/CRCUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
public class CRCUtil {
public static void crc16(byte crc[], byte data[], int index, int len) {

2
src/main/java/wiki/tall/ccmq/common/util/DateUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/DateUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import java.util.TimeZone;

87
src/main/java/com/ccsens/opensource/wxconfigurer/util/FileUtil.java

@ -0,0 +1,87 @@
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.lang.Console;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
/**
* @author __zHangSan
*/
public class FileUtil {
public static String getFileMD5(String file) {
String str = null;
FileInputStream in = null;
try {
in = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[1024 * 1024 * 10];
int len = 0;
while ((len = in.read(buffer)) > 0) {
digest.update(buffer, 0, len);
}
String md5 = new BigInteger(1, digest.digest()).toString(16);
int length = 32 - md5.length();
if (length > 0) {
for (int i = 0; i < length; i++) {
md5 = "0" + md5;
}
}
str = md5;
} catch (Exception e) {
System.out.println(e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
System.out.println(e);
}
}
return str;
}
public static String getFileSha1(String file) {
String str = null;
FileInputStream in = null;
try {
in = new FileInputStream(file);
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] buffer = new byte[1024 * 1024 * 10];
int len = 0;
while ((len = in.read(buffer)) > 0) {
digest.update(buffer, 0, len);
}
String sha1 = new BigInteger(1, digest.digest()).toString(16);
int length = 40 - sha1.length();
if (length > 0) {
for (int i = 0; i < length; i++) {
sha1 = "0" + sha1;
}
}
str = sha1;
} catch (Exception e) {
System.out.println(e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e) {
System.out.println(e);
}
}
return str;
}
public static void main(String[] args) {
String path = "D:\\cygwin64\\Cygwin.ico";
Console.log(getFileMD5(path));
Console.log(getFileSha1(path));
}
}

2
src/main/java/wiki/tall/ccmq/common/util/GenericsUtils.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/GenericsUtils.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

2
src/main/java/wiki/tall/ccmq/common/util/HttpServletUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpServletUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

2
src/main/java/wiki/tall/ccmq/common/util/HttpsClientRequestFactory.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpsClientRequestFactory.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.springframework.http.client.SimpleClientHttpRequestFactory;

2
src/main/java/wiki/tall/ccmq/common/util/HttpsUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/HttpsUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;

2
src/main/java/wiki/tall/ccmq/common/util/IDGenerator.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/IDGenerator.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.lang.Snowflake;
import org.springframework.beans.factory.annotation.Autowired;

2
src/main/java/wiki/tall/ccmq/common/util/ImgUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/ImgUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import sun.font.FontDesignMetrics;

2
src/main/java/wiki/tall/ccmq/common/util/JacksonUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/JacksonUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;

2
src/main/java/wiki/tall/ccmq/common/util/JsonPathUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonPathUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.Option;

62
src/main/java/wiki/tall/ccmq/common/util/JsonResponse.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonResponse.java

@ -1,9 +1,12 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author __zHangSan
*/
@Data
@ApiModel
public class JsonResponse<T> {
@ -50,50 +53,54 @@ public class JsonResponse<T> {
private JsonResponse() {
}
public static JsonResponse newInstance(){
return new JsonResponse();
public static JsonResponse<String> newInstance(){
return new JsonResponse<String>();
}
public JsonResponse ok(){
public static <T> JsonResponse<T> newInstance(Class<T> clazz){
return new JsonResponse<T>();
}
public JsonResponse<T> ok(){
this.code = RegularError.ERRCODE_SUCCESS;
this.msg = RegularError.ERRCODE_SUCCESS_PHASE;
this.success = true;
return this;
}
public JsonResponse ok(T data){
public JsonResponse<T> ok(T data){
ok();
this.data = data;
return this;
}
public JsonResponse fail(){
public JsonResponse<T> fail(){
this.code = RegularError.ERRCODE_FAIL;
this.msg = RegularError.ERRCODE_FAIL_PHASE;
this.success = false;
return this;
}
public JsonResponse fail(String error){
public JsonResponse<T> fail(String error){
fail();
this.msg = error;
return this;
}
public JsonResponse fail(int code){
public JsonResponse<T> fail(int code){
fail();
this.code = code;
return this;
}
public JsonResponse fail(int code, String error){
public JsonResponse<T> fail(int code, String error){
fail();
this.code = code;
this.msg = error;
return this;
}
public JsonResponse fail(int code, String error, T obj){
public JsonResponse<T> fail(int code, String error, T obj){
fail();
this.code = code;
this.msg = error;
@ -101,57 +108,60 @@ public class JsonResponse<T> {
return this;
}
//Token null
public JsonResponse tokenNotFound(){
/**
* Token Null
* @return
*/
public JsonResponse<T> tokenNotFound(){
fail(TokenError.TOKEN_ERRCODE_NOTFOUND, TokenError.TOKEN_ERRCODE_NOTFOUND_PHASE);
return this;
}
//Token expire
public JsonResponse tokenExpire(){
/**
* Token expire
* @return
*/
public JsonResponse<T> tokenExpire(){
fail(TokenError.TOKEN_ERRCODE_EXPIRE, TokenError.TOKEN_ERRCODE_EXPIRE_PHASE);
return this;
}
public JsonResponse tokenExpire(String phase){
public JsonResponse<T> tokenExpire(String phase){
fail(TokenError.TOKEN_ERRCODE_EXPIRE,phase);
return this;
}
public JsonResponse tokenSignatureFail(){
public JsonResponse<T> tokenSignatureFail(){
fail(TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE, TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE_PHASE);
return this;
}
public JsonResponse tokenSignatureFail(String phase){
public JsonResponse<T> tokenSignatureFail(String phase){
fail(TokenError.TOKEN_ERRCODE_SIGNATURE_INVALIDATE,phase);
return this;
}
//Token Stub Not Found
public JsonResponse tokenStubNotFound(){
public JsonResponse<T> tokenStubNotFound(){
fail(TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND, TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND_PHASE);
return this;
}
public JsonResponse tokenDisabled(String phase){
public JsonResponse<T> tokenDisabled(String phase){
fail(TokenError.TOKEN_ERRCODE_STUB_NOT_FOUND,phase);
return this;
}
//User Disabled
public JsonResponse userDisabled(){
public JsonResponse<T> userDisabled(){
fail(TokenError.TOKEN_ERRCODE_USER_DISABLED, TokenError.TOKEN_ERRCODE_USER_DISABLED_PHASE);
return this;
}
public JsonResponse userDisabled(String phase){
public JsonResponse<T> userDisabled(String phase){
fail(TokenError.TOKEN_ERRCODE_USER_DISABLED,phase);
return this;
}
//Token Failed
public JsonResponse tokenFailed(){
public JsonResponse<T> tokenFailed(){
fail(TokenError.TOKEN_ERRCODE_FAILED, TokenError.TOKEN_ERRCODE_FAILED_PHASE);
return this;
}
public JsonResponse tokenFailed(String phase){
public JsonResponse<T> tokenFailed(String phase){
fail(TokenError.TOKEN_ERRCODE_FAILED,phase);
return this;
}

2
src/main/java/wiki/tall/ccmq/common/util/JsonResponse1.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/JsonResponse1.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import java.util.ArrayList;
import java.util.Collection;

2
src/main/java/wiki/tall/ccmq/common/util/JwtUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/JwtUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;

2
src/main/java/wiki/tall/ccmq/common/util/MyJacksonObjectMapper.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/MyJacksonObjectMapper.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;

2
src/main/java/wiki/tall/ccmq/common/util/NotSupportedFileTypeException.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/NotSupportedFileTypeException.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
public class NotSupportedFileTypeException extends Exception {
public NotSupportedFileTypeException() {

2
src/main/java/wiki/tall/ccmq/common/util/PasswordEncryptionUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/PasswordEncryptionUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

2
src/main/java/wiki/tall/ccmq/common/util/RedisKeyManager.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/RedisKeyManager.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
/**
* @author __zHangSan

3
src/main/java/wiki/tall/ccmq/common/util/RedisUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/RedisUtil.java

@ -1,6 +1,5 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.List;

2
src/main/java/wiki/tall/ccmq/common/util/ResourceLock.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/ResourceLock.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

2
src/main/java/wiki/tall/ccmq/common/util/SQLUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/SQLUtil.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import java.io.BufferedReader;
import java.io.InputStream;

188
src/main/java/com/ccsens/opensource/wxconfigurer/util/ServletUtil.java

@ -0,0 +1,188 @@
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.*;
import java.net.URL;
import java.util.Date;
/**
* @author __zHangSan
*/
public class ServletUtil extends cn.hutool.extra.servlet.ServletUtil {
public static void responseJson(HttpServletResponse response, String json) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.append(json);
out.close();
}
/**
* 获取用户真实IP地址不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
*
* 可是如果通过了多级反向代理的话X-Forwarded-For的值并不止一个而是一串IP值究竟哪个才是真正的用户端的真实IP呢
* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串
*
* X-Forwarded-For192.168.1.110, 192.168.1.120, 192.168.1.130,
* 192.168.1.100
*
* 用户真实IP为 192.168.1.110
*
* @param request
* @return
*/
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public static String getSiteBasePath(HttpServletRequest request){
return request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort();
}
public static String getSiteContextPath(HttpServletRequest request){
return getSiteBasePath(request) + "/" + request.getContextPath();
}
/**
* Servlet3.0文件上传 (生成uuid.txt文件)
* @param part Servlet3.0 API ---> Part
* @param allowedExts 允许的后缀名
* @param dir 目录
* @return 文件名
* @throws IOException
* @throws NotSupportedFileTypeException
*/
public static String uploadFile(Part part, String allowedExts, String dir)
throws IOException, NotSupportedFileTypeException {
String extraPath = DateUtil.format(new Date(), "yyyyMMdd");
//1.判断是否为空
if (part == null) {
return null;
}
//2.生成文件名 [uuid.ext]
String original = getFileNameByPart(part);
String ext = FileUtil.extName(original);
if (StrUtil.isEmpty(ext) || !allowedExts.contains(ext)) {
throw new NotSupportedFileTypeException("Not Supported File Type: " + ext);
}
String path = extraPath + File.separator + SecureUtil.simpleUUID() + "." + ext;
//3.创建必要的目录
File tmpFile = new File(dir);
if (!tmpFile.exists()) {
tmpFile.mkdirs();
}
//4.写入磁盘
String fullPath = dir + File.separator + path;
FileUtil.writeFromStream(part.getInputStream(),fullPath);
return path;
}
public static byte[] uploadFile(Part part)
throws IOException, NotSupportedFileTypeException {
//1.判断是否为空
if (part == null) {
return null;
}
//2.读取文件
InputStream is = part.getInputStream();
byte[] bytes = new byte[is.available()];
is.read(bytes);
return bytes;
}
/**
* 根据请求头解析出文件名
* 请求头的格式火狐和google浏览器下form-data; name="file"; filename="snmp4j--api.zip"
* IE浏览器下form-data; name="file"; filename="E:\snmp4j--api.zip"
* @param part 包含请求头
* @return 文件名
*/
public static String getFileNameByPart(Part part) {
String header = part.getHeader("content-disposition");
/**
* String[] tempArr1 = header.split(";");代码执行完之后在不同的浏览器下tempArr1数组里面的内容稍有区别
* 火狐或者google浏览器下tempArr1={form-data,name="file",filename="snmp4j--api.zip"}
* IE浏览器下tempArr1={form-data,name="file",filename="E:\snmp4j--api.zip"}
*/
String[] tempArr1 = header.split(";");
/**
*火狐或者google浏览器下tempArr2={filename,"snmp4j--api.zip"}
*IE浏览器下tempArr2={filename,"E:\snmp4j--api.zip"}
*/
String[] tempArr2 = tempArr1[2].split("=");
//获取文件名,兼容各种浏览器的写法
String fileName = tempArr2[1].substring(tempArr2[1].lastIndexOf("\\")+1).replaceAll("\"", "");
return fileName;
}
public static void downLoadFile(HttpServletResponse response, String filePath, String filename, String contentType,boolean isOnLine) throws Exception {
File f = new File(filePath);
if (!f.exists()) {
response.sendError(404, "File not found!");
return;
}
if(StrUtil.isEmpty(filename)){
filename = f.getName();
}
if(StrUtil.isEmpty(contentType)){
contentType = "application/octet-stream";
}
BufferedInputStream br = new BufferedInputStream(new FileInputStream(f));
byte[] buf = new byte[1024];
int len = 0;
// 非常重要
response.reset();
if (isOnLine) {
URL u = new URL("file:///" + filePath);
response.setContentType(u.openConnection().getContentType());
response.setHeader("Content-Disposition", "inline; filename=" + filename);
// 文件名应该编码成UTF-8
} else {
response.setContentType(contentType);
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
}
Console.log("filename: {}",filename);
OutputStream out = response.getOutputStream();
while ((len = br.read(buf)) > 0) {
out.write(buf, 0, len);
}
br.close();
out.close();
}
}

2
src/main/java/wiki/tall/ccmq/common/util/ShiroKit.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/ShiroKit.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;

2
src/main/java/wiki/tall/ccmq/common/util/SpringContextUtils.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/SpringContextUtils.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;

2
src/main/java/wiki/tall/ccmq/common/util/TokenBean.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/TokenBean.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import lombok.Data;

2
src/main/java/wiki/tall/ccmq/common/util/TokenBean1.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/TokenBean1.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
public class TokenBean1 {
private String access_token;

2
src/main/java/wiki/tall/ccmq/common/util/ToolUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/ToolUtil.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import java.util.Random;

2
src/main/java/wiki/tall/ccmq/common/util/WebConstant.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/WebConstant.java

@ -1,4 +1,4 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.codec.Base64;

47
src/main/java/wiki/tall/ccmq/common/util/WechatUtil.java → src/main/java/com/ccsens/opensource/wxconfigurer/util/WechatUtil.java

@ -1,7 +1,8 @@
package wiki.tall.ccmq.common.util;
package com.ccsens.opensource.wxconfigurer.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ccsens.opensource.wxconfigurer.bean.po.WxAccessToken;
import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
@ -46,17 +47,6 @@ public class WechatUtil {
public String errmsg;
}
/**
* Access_Token
*/
public static class WechatToken{
public String access_token;
public Long expires_in;
public String errcode;
public String errmsg;
public Date create_at;
}
/**
* Get wx code
*/
@ -135,11 +125,13 @@ public class WechatUtil {
= "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
private static final String URL_PAY_TO_USR_BANK
= "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank";
private static final String URL_CREATE_MENU
= " https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%1$s";
private static final String appid = "wx356e01c7eb01d55d";
private static final String secret = "83149f3e151d9532f1d2eb76ca10722b";
private static final String mchid = "1513955071";
private static final String key = "5a689a2d6b8c4ff499c23d998fde0941"; //商户平台key
private static final String appid = "wx7af1bf1e14facf82";
private static final String secret = "a6613fae11b497639c0224b820aaf6d9";
private static final String mchid = "";
private static final String key = "";
/**
* 小程序登陆根据code获取用户信息(openid,sessionKey,)
@ -168,23 +160,6 @@ public class WechatUtil {
return wechatUser;
}
/**
* 获取Access_token
* @return
* @throws Exception
*/
public static WechatToken getAccessToken() throws Exception {
String response = null;
WechatToken wechatToken = null;
String url = String.format(URL_GET_ACCESS_TOKEN,"client_credential",appid,secret);
response = HttpsUtil.httpsRequest(url,"GET",null);
if(StrUtil.isEmpty(response))
return null;
wechatToken = JacksonUtil.jsonToBean(response,WechatToken.class);
wechatToken.create_at = new Date();
return wechatToken;
}
/**
* 获取小程序二维码/小程序码
@ -192,8 +167,7 @@ public class WechatUtil {
* @throws Exception
*/
public static void getWxCode(String page,String scene,String path) throws Exception {
WechatToken wechatToken = getAccessToken();
String url = String.format(URL_GET_WX_CODE_B,wechatToken.access_token);
String url = String.format(URL_GET_WX_CODE_B,WxUtil.getAccessToken());
WechatCode wechatCode = new WechatCode();
wechatCode.page = page;
@ -290,7 +264,8 @@ public class WechatUtil {
public static String createSign(Map<String,Object> sParaTemp){
// 除去数组中的空值和签名参数
Map sPara = paraFilter(sParaTemp);
String prestr = createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
// 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String prestr = createLinkString(sPara);
//MD5运算生成签名
String sign = sign(prestr, key, "utf-8").toUpperCase();
return sign;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save