正在显示
35 个修改的文件
包含
1842 行增加
和
11 行删除
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | <project version="4"> | 2 | <project version="4"> |
| 3 | <component name="CompilerConfiguration"> | 3 | <component name="CompilerConfiguration"> |
| 4 | <annotationProcessing> | 4 | <annotationProcessing> |
| 5 | + <profile default="true" name="Default" enabled="true" /> | ||
| 5 | <profile name="Maven default annotation processors profile" enabled="true"> | 6 | <profile name="Maven default annotation processors profile" enabled="true"> |
| 6 | <sourceOutputDir name="target/generated-sources/annotations" /> | 7 | <sourceOutputDir name="target/generated-sources/annotations" /> |
| 7 | <sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> | 8 | <sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> |
此 diff 太大无法显示。
| @@ -12,23 +12,71 @@ | @@ -12,23 +12,71 @@ | ||
| 12 | <artifactId>lh-mqtt-service</artifactId> | 12 | <artifactId>lh-mqtt-service</artifactId> |
| 13 | 13 | ||
| 14 | <dependencies> | 14 | <dependencies> |
| 15 | + <!-- 通用工具--> | ||
| 15 | <dependency> | 16 | <dependency> |
| 16 | - <groupId>com.luhui.lyl.device.service</groupId> | ||
| 17 | - <artifactId>ly-device-mqtt-comm</artifactId> | ||
| 18 | - <version>${project.parent.version}</version> | 17 | + <groupId>com.zhonglai.luhui</groupId> |
| 18 | + <artifactId>ruoyi-common</artifactId> | ||
| 19 | + </dependency> | ||
| 20 | + | ||
| 21 | + <!-- 文档 --> | ||
| 22 | + <dependency> | ||
| 23 | + <groupId>io.springfox</groupId> | ||
| 24 | + <artifactId>springfox-swagger2</artifactId> | ||
| 25 | + <version>${swagger.version}</version> | ||
| 19 | <exclusions> | 26 | <exclusions> |
| 20 | <exclusion> | 27 | <exclusion> |
| 21 | - <groupId>org.springframework.boot</groupId> | ||
| 22 | - <artifactId>**</artifactId> | 28 | + <groupId>io.swagger</groupId> |
| 29 | + <artifactId>swagger-models</artifactId> | ||
| 30 | + </exclusion> | ||
| 31 | + <exclusion> | ||
| 32 | + <groupId>com.google.guava</groupId> | ||
| 33 | + <artifactId>guava</artifactId> | ||
| 23 | </exclusion> | 34 | </exclusion> |
| 24 | </exclusions> | 35 | </exclusions> |
| 25 | </dependency> | 36 | </dependency> |
| 37 | + <!--https://mvnrepository.com/artifact/io.swagger/swagger-models--> | ||
| 38 | + <dependency> | ||
| 39 | + <groupId>io.swagger</groupId> | ||
| 40 | + <artifactId>swagger-models</artifactId> | ||
| 41 | + <version>${swagger-models.version}</version> | ||
| 42 | + </dependency> | ||
| 43 | + <dependency> | ||
| 44 | + <groupId>io.springfox</groupId> | ||
| 45 | + <artifactId>springfox-swagger-ui</artifactId> | ||
| 46 | + <version>${swagger.version}</version> | ||
| 47 | + </dependency> | ||
| 48 | + <!--<!– https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui –>--> | ||
| 49 | + <dependency> | ||
| 50 | + <groupId>com.github.xiaoymin</groupId> | ||
| 51 | + <artifactId>swagger-bootstrap-ui</artifactId> | ||
| 52 | + <version>${swagger-ui.version}</version> | ||
| 53 | + </dependency> | ||
| 54 | + | ||
| 55 | + <dependency> | ||
| 56 | + <groupId>tk.mybatis</groupId> | ||
| 57 | + <artifactId>mapper</artifactId> | ||
| 58 | + <!-- 建议使用最新版本,最新版本请从项目首页查找 --> | ||
| 59 | + </dependency> | ||
| 60 | + <dependency> | ||
| 61 | + <groupId>tk.mybatis</groupId> | ||
| 62 | + <artifactId>mapper-spring-boot-starter</artifactId> | ||
| 63 | + </dependency> | ||
| 64 | + | ||
| 65 | + <!-- mqtt --> | ||
| 66 | + <dependency> | ||
| 67 | + <groupId>org.eclipse.paho</groupId> | ||
| 68 | + <artifactId>org.eclipse.paho.client.mqttv3</artifactId> | ||
| 69 | + </dependency> | ||
| 26 | 70 | ||
| 27 | <dependency> | 71 | <dependency> |
| 28 | <groupId>org.springframework.boot</groupId> | 72 | <groupId>org.springframework.boot</groupId> |
| 29 | <artifactId>spring-boot-starter-tomcat</artifactId> | 73 | <artifactId>spring-boot-starter-tomcat</artifactId> |
| 30 | <scope>provided</scope> | 74 | <scope>provided</scope> |
| 31 | </dependency> | 75 | </dependency> |
| 76 | + <dependency> | ||
| 77 | + <groupId>net.jodah</groupId> | ||
| 78 | + <artifactId>expiringmap</artifactId> | ||
| 79 | + </dependency> | ||
| 32 | </dependencies> | 80 | </dependencies> |
| 33 | 81 | ||
| 34 | <build> | 82 | <build> |
| 1 | +package com.zhonglai.luhui.mqtt.comm.agreement; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreement; | ||
| 4 | +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreementFactory; | ||
| 5 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 6 | +import org.springframework.stereotype.Component; | ||
| 7 | + | ||
| 8 | +import java.util.Map; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 业务协议工厂实现 | ||
| 12 | + */ | ||
| 13 | +@Component | ||
| 14 | +public class BusinessAgreementFactoryImpl implements BusinessAgreementFactory { | ||
| 15 | + @Autowired | ||
| 16 | + private Map<String, BusinessAgreement> businessAgreementMap; | ||
| 17 | + | ||
| 18 | + @Override | ||
| 19 | + public BusinessAgreement createBusinessAgreement(String topicType) { | ||
| 20 | + BusinessAgreement businessAgreement = businessAgreementMap.get(topicType.toUpperCase()); | ||
| 21 | + if(null == businessAgreement) //没有找到就用默认的 | ||
| 22 | + { | ||
| 23 | + businessAgreementMap.get("DEFAULT"); | ||
| 24 | + } | ||
| 25 | + return businessAgreement; | ||
| 26 | + } | ||
| 27 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/agreement/DefaultAgreement.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.agreement; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto; | ||
| 4 | +import com.zhonglai.luhui.mqtt.comm.factory.BusinessAgreement; | ||
| 5 | +import com.zhonglai.luhui.mqtt.comm.factory.Topic; | ||
| 6 | +import org.slf4j.Logger; | ||
| 7 | +import org.slf4j.LoggerFactory; | ||
| 8 | +import org.springframework.stereotype.Service; | ||
| 9 | + | ||
| 10 | +import java.nio.charset.Charset; | ||
| 11 | + | ||
| 12 | +@Service("DEFAULT") | ||
| 13 | +public class DefaultAgreement implements BusinessAgreement<byte[]> { | ||
| 14 | + private static final Logger log = LoggerFactory.getLogger(DefaultAgreement.class); | ||
| 15 | + | ||
| 16 | + @Override | ||
| 17 | + public ServerDto analysis(Topic topic, byte[] data) throws Exception { | ||
| 18 | + log.info("未知的topic{} payload{}",topic, new String(data, Charset.forName("UTF-8"))); | ||
| 19 | + return null; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Override | ||
| 23 | + public byte[] toData(byte[] data) { | ||
| 24 | + return data; | ||
| 25 | + } | ||
| 26 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.clien; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.ruoyi.common.core.domain.Message; | ||
| 5 | +import com.zhonglai.luhui.mqtt.comm.dto.ServerAgreementContent; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 客户端链接 | ||
| 9 | + */ | ||
| 10 | +public interface ClienConnection { | ||
| 11 | + public void close(); | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * 回复 | ||
| 15 | + * @param agreementContent | ||
| 16 | + */ | ||
| 17 | + public void reply(ServerAgreementContent agreementContent); | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 回复 | ||
| 21 | + */ | ||
| 22 | + public Message getReplyMessage(); | ||
| 23 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/clien/impl/ClienConnectionImpl.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.clien.impl; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.ruoyi.common.core.domain.Message; | ||
| 5 | +import com.ruoyi.common.core.domain.MessageCode; | ||
| 6 | +import com.ruoyi.common.core.domain.MessageCodeType; | ||
| 7 | +import com.zhonglai.luhui.mqtt.comm.clien.ClienConnection; | ||
| 8 | +import com.zhonglai.luhui.mqtt.comm.dto.ServerAgreementContent; | ||
| 9 | + | ||
| 10 | +public class ClienConnectionImpl implements ClienConnection { | ||
| 11 | + private Message message = new Message(); | ||
| 12 | + | ||
| 13 | + @Override | ||
| 14 | + public void close() { | ||
| 15 | + this.message.setCode(MessageCode.DEFAULT_FAIL_CODE); | ||
| 16 | + this.message.setMessage("链接超时关闭"); | ||
| 17 | + this.notify(); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public void reply(ServerAgreementContent agreementContent) { | ||
| 22 | + agreementContent.setReplyMessage(message); | ||
| 23 | + this.notify(); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Override | ||
| 27 | + public Message getReplyMessage() { | ||
| 28 | + return message; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public ClienConnectionImpl setCode(MessageCodeType code) | ||
| 32 | + { | ||
| 33 | + this.message.setCode(code); | ||
| 34 | + return this; | ||
| 35 | + } | ||
| 36 | + public ClienConnectionImpl setData(Object data) | ||
| 37 | + { | ||
| 38 | + this.message.setData(data); | ||
| 39 | + return this; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public ClienConnectionImpl setMessage(String message) | ||
| 43 | + { | ||
| 44 | + this.message.setMessage(message); | ||
| 45 | + return this; | ||
| 46 | + } | ||
| 47 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/config/ControllerLogAspect.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.config; | ||
| 2 | + | ||
| 3 | +import org.aspectj.lang.JoinPoint; | ||
| 4 | +import org.aspectj.lang.annotation.AfterReturning; | ||
| 5 | +import org.aspectj.lang.annotation.Aspect; | ||
| 6 | +import org.aspectj.lang.annotation.Before; | ||
| 7 | +import org.aspectj.lang.annotation.Pointcut; | ||
| 8 | +import org.slf4j.Logger; | ||
| 9 | +import org.slf4j.LoggerFactory; | ||
| 10 | +import org.springframework.stereotype.Component; | ||
| 11 | +import org.springframework.web.context.request.RequestContextHolder; | ||
| 12 | +import org.springframework.web.context.request.ServletRequestAttributes; | ||
| 13 | + | ||
| 14 | +import javax.servlet.http.HttpServletRequest; | ||
| 15 | +import java.util.Enumeration; | ||
| 16 | +import java.util.HashMap; | ||
| 17 | +import java.util.Map; | ||
| 18 | + | ||
| 19 | +@Aspect | ||
| 20 | +@Component | ||
| 21 | +public class ControllerLogAspect { | ||
| 22 | + private Logger logger = LoggerFactory.getLogger(this.getClass()); | ||
| 23 | + | ||
| 24 | + @Pointcut("execution(public * com.zhonglai.luhui.mqtt.*.controller..*.*(..))") | ||
| 25 | + public void controllerLog() { | ||
| 26 | + | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Before("controllerLog()") | ||
| 30 | + public void doBefore(JoinPoint joinPoint) | ||
| 31 | + { | ||
| 32 | + // 接收到请求,记录请求内容 | ||
| 33 | + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | ||
| 34 | + HttpServletRequest request = attributes.getRequest(); | ||
| 35 | + Enumeration<String> hs = request.getHeaderNames(); | ||
| 36 | + Map<String,Object> map = new HashMap<>(); | ||
| 37 | + while (hs.hasMoreElements()) | ||
| 38 | + { | ||
| 39 | + String key= hs.nextElement(); | ||
| 40 | + map.put(key,request.getHeader(key)); | ||
| 41 | + } | ||
| 42 | + logger.info("请求url:{},请求head:{},请求IP:{},请求方法:{},请求参数:{}",request.getRequestURL(),map,request.getRemoteAddr(),joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(),joinPoint.getArgs()); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @AfterReturning(returning = "ret", pointcut = "controllerLog()") | ||
| 46 | + public void doAfterReturning(Object ret) { | ||
| 47 | + // 处理完请求,返回内容 | ||
| 48 | + logger.info("返回 {} " , GsonConstructor.get().toJson(ret)); | ||
| 49 | + } | ||
| 50 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.config; | ||
| 2 | + | ||
| 3 | +import org.springframework.context.annotation.Bean; | ||
| 4 | +import org.springframework.context.annotation.Configuration; | ||
| 5 | +import org.springframework.web.cors.CorsConfiguration; | ||
| 6 | +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
| 7 | +import org.springframework.web.filter.CorsFilter; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 跨域配置,需要和WebMvcConfigurerAdapter的addCorsMappings配合使用 | ||
| 11 | + */ | ||
| 12 | +@Configuration | ||
| 13 | +public class CorsConfig { | ||
| 14 | + private CorsConfiguration buildConfig() { | ||
| 15 | + CorsConfiguration corsConfiguration = new CorsConfiguration(); | ||
| 16 | + corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用 | ||
| 17 | + corsConfiguration.addAllowedHeader("*"); // 2允许任何头 | ||
| 18 | + corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等) | ||
| 19 | + return corsConfiguration; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + @Bean | ||
| 23 | + public CorsFilter corsFilter() { | ||
| 24 | + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
| 25 | + source.registerCorsConfiguration("/**", buildConfig()); // 4 | ||
| 26 | + return new CorsFilter(source); | ||
| 27 | + } | ||
| 28 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.config; | ||
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.annotation.JsonAutoDetect; | ||
| 4 | +import com.fasterxml.jackson.annotation.PropertyAccessor; | ||
| 5 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 6 | +import org.springframework.beans.factory.annotation.Value; | ||
| 7 | +import org.springframework.context.annotation.Bean; | ||
| 8 | +import org.springframework.context.annotation.Configuration; | ||
| 9 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
| 10 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 11 | +import org.springframework.data.redis.listener.PatternTopic; | ||
| 12 | +import org.springframework.data.redis.listener.RedisMessageListenerContainer; | ||
| 13 | +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; | ||
| 14 | +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | ||
| 15 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
| 16 | + | ||
| 17 | +@Configuration | ||
| 18 | +public class RedisConfig { | ||
| 19 | + @Value("${sys.redis.field:#{'luhui:mqttservice:device:'}") | ||
| 20 | + public static String FIELD ; //域 | ||
| 21 | + | ||
| 22 | + @Bean | ||
| 23 | + public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){ | ||
| 24 | + RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); | ||
| 25 | + //设置工厂链接 | ||
| 26 | + redisTemplate.setConnectionFactory(redisConnectionFactory); | ||
| 27 | + //设置自定义序列化方式 | ||
| 28 | + setSerializeConfig(redisTemplate, redisConnectionFactory); | ||
| 29 | + return redisTemplate; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + private void setSerializeConfig(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory redisConnectionFactory) { | ||
| 33 | + //对字符串采取普通的序列化方式 适用于key 因为我们一般采取简单字符串作为key | ||
| 34 | + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); | ||
| 35 | + //普通的string类型的key采用 普通序列化方式 | ||
| 36 | + redisTemplate.setKeySerializer(stringRedisSerializer); | ||
| 37 | + //普通hash类型的key也使用 普通序列化方式 | ||
| 38 | + redisTemplate.setHashKeySerializer(stringRedisSerializer); | ||
| 39 | + //解决查询缓存转换异常的问题 大家不能理解就直接用就可以了 这是springboot自带的jackson序列化类,但是会有一定问题 | ||
| 40 | + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); | ||
| 41 | + ObjectMapper om = new ObjectMapper(); | ||
| 42 | + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | ||
| 43 | + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); | ||
| 44 | + jackson2JsonRedisSerializer.setObjectMapper(om); | ||
| 45 | + //普通的值采用jackson方式自动序列化 | ||
| 46 | + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); | ||
| 47 | + //hash类型的值也采用jackson方式序列化 | ||
| 48 | + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); | ||
| 49 | + //属性设置完成afterPropertiesSet就会被调用,可以对设置不成功的做一些默认处理 | ||
| 50 | + redisTemplate.afterPropertiesSet(); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * redis消息监听 | ||
| 56 | + * @param connectionFactory | ||
| 57 | + * @return | ||
| 58 | + */ | ||
| 59 | + @Bean | ||
| 60 | + public RedisMessageListenerContainer messageListenerContainer(RedisConnectionFactory connectionFactory) { | ||
| 61 | + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | ||
| 62 | + container.setConnectionFactory(connectionFactory); | ||
| 63 | + container.addMessageListener(new MessageListenerAdapter(), new PatternTopic(getRedisKeyPath()+"**")); | ||
| 64 | + | ||
| 65 | + return container; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + public static String getRedisKeyPath() | ||
| 69 | + { | ||
| 70 | + return FIELD+ RedisKeyMqttUser.ROLEID+":"+ RedisKeyMqttUser.USERNAME+":"; | ||
| 71 | + } | ||
| 72 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.config; | ||
| 2 | + | ||
| 3 | +import org.springframework.beans.factory.annotation.Value; | ||
| 4 | +import org.springframework.stereotype.Component; | ||
| 5 | + | ||
| 6 | +@Component | ||
| 7 | +public class RedisKeyMqttUser { | ||
| 8 | + public static String ROLEID; | ||
| 9 | + public static String USERNAME; | ||
| 10 | + | ||
| 11 | + @Value("${mqtt.redis.key.roleid}") | ||
| 12 | + public void setROLEID(String roleid) { | ||
| 13 | + ROLEID = roleid; | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + @Value("${mqtt.redis.key.username}") | ||
| 17 | + public void setUSERNAME(String username) { | ||
| 18 | + USERNAME = username; | ||
| 19 | + } | ||
| 20 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.config; | ||
| 2 | + | ||
| 3 | +import com.alibaba.fastjson.JSONObject; | ||
| 4 | +import com.ruoyi.common.utils.http.HttpUtils; | ||
| 5 | +import org.slf4j.Logger; | ||
| 6 | +import org.slf4j.LoggerFactory; | ||
| 7 | +import org.springframework.beans.factory.annotation.Value; | ||
| 8 | +import org.springframework.context.annotation.Configuration; | ||
| 9 | + | ||
| 10 | +import javax.annotation.PostConstruct; | ||
| 11 | + | ||
| 12 | +@Configuration | ||
| 13 | +public class SysParameter { | ||
| 14 | + private static Logger log = LoggerFactory.getLogger(SysParameter.class); | ||
| 15 | + | ||
| 16 | + public static String service_ip = ""; //服务所在地址 | ||
| 17 | + | ||
| 18 | + @Value("${mqtt.topicconfig:/{{roleid}}/{{username}}/{{clientid}}/{{topicType}}/{{messageid}}}") | ||
| 19 | + public String tempTopicconfig ; //topic 配置 | ||
| 20 | + | ||
| 21 | + public static String topicconfig ; //topic 配置 | ||
| 22 | + | ||
| 23 | + @PostConstruct | ||
| 24 | + public static void init() { | ||
| 25 | + String service_ip_url = "http://ly.userlogin.yu2le.com/ip"; | ||
| 26 | + JSONObject jsonObject = JSONObject.parseObject(HttpUtils.sendGet(service_ip_url)); | ||
| 27 | + service_ip = jsonObject.getString("data"); | ||
| 28 | + log.info("服务器地址:{}",service_ip); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + public void inittopicconfig() | ||
| 32 | + { | ||
| 33 | + topicconfig = tempTopicconfig; | ||
| 34 | + } | ||
| 35 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.dto; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.mqtt.comm.config.SysParameter; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +@Data | ||
| 7 | +public class DeviceInfoDto { | ||
| 8 | + private String id; // varchar(50) NOT NULL COMMENT '设备编号,如:设备imei号_传感器编号', | ||
| 9 | + private String deviceId; // varchar(50) NOT NULL COMMENT '设备IMEI号', | ||
| 10 | + private String deviceName; // varchar(50) DEFAULT NULL COMMENT '设备名称', | ||
| 11 | + private String sensorNumber; // varchar(10) NOT NULL DEFAULT '01' COMMENT '传感器编号,默认01', | ||
| 12 | + private String deviceModel; // varchar(10) NOT NULL COMMENT '设备型号,(3,5,6)', | ||
| 13 | + private String deviceType; // varchar(10) DEFAULT NULL COMMENT '设备类型(0传感器,1开关控制器)', | ||
| 14 | + private String online; // varchar(10) DEFAULT NULL COMMENT '设备在线状态(00-不在线,01-在线)', | ||
| 15 | + private Integer createTime; // int(11) DEFAULT NULL COMMENT '添加时间', | ||
| 16 | + private String alarmCode; // varchar(10) DEFAULT NULL COMMENT '设备告警状态', | ||
| 17 | + private Integer dataUpdateTime; // int(11) DEFAULT NULL COMMENT '数据更新时间', | ||
| 18 | + private String dataState; // text COMMENT '数据状态(json串)', | ||
| 19 | + private Integer alarmLastCreateTime; // int(11) DEFAULT NULL COMMENT '告警最后一次产生的时间', | ||
| 20 | + private String deviceServiceIp; //设备链接服务器地址 | ||
| 21 | + private Integer alarmSqlApply; //告警sql规则申请(0-未申请,1-已申请) | ||
| 22 | + private String deviceConfig; //设备配置 | ||
| 23 | + private String sensorType; //设备配置 | ||
| 24 | + private Boolean isadd; //是否添加 | ||
| 25 | + | ||
| 26 | + public static DeviceInfoDto newDefaultDeviceInfoDto(String imei, String sensor_number, String device_model, String device_type) | ||
| 27 | + { | ||
| 28 | + DeviceInfoDto deviceInfoDto = new DeviceInfoDto(); | ||
| 29 | + deviceInfoDto.setId(imei+"_"+sensor_number); | ||
| 30 | + deviceInfoDto.setDeviceId(imei); | ||
| 31 | + deviceInfoDto.setSensorNumber(sensor_number); | ||
| 32 | + deviceInfoDto.setDeviceModel(device_model); | ||
| 33 | + deviceInfoDto.setDeviceType(device_type); | ||
| 34 | + deviceInfoDto.setAlarmCode("00"); | ||
| 35 | + deviceInfoDto.setOnline("01"); | ||
| 36 | + deviceInfoDto.setDeviceServiceIp(SysParameter.service_ip); | ||
| 37 | + deviceInfoDto.setIsadd(true); | ||
| 38 | + return deviceInfoDto; | ||
| 39 | + } | ||
| 40 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +@Data | ||
| 6 | +public class MyException extends RuntimeException{ | ||
| 7 | + private static final long serialVersionUID = 8827598182853467258L; | ||
| 8 | + private Message errmge; | ||
| 9 | + | ||
| 10 | + public MyException(Message myMessage) { | ||
| 11 | + super(myMessage.getMessage()); | ||
| 12 | + this.errmge = myMessage; | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + public MyException(String message, Throwable cause) { | ||
| 16 | + super(message, cause); | ||
| 17 | + } | ||
| 18 | + public MyException(String message) { | ||
| 19 | + super(message); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/dto/ServerAgreementContent.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.dto; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.zhonglai.luhui.mqtt.comm.factory.Topic; | ||
| 5 | + | ||
| 6 | +public interface ServerAgreementContent { | ||
| 7 | + String getClienConnectionId(); | ||
| 8 | + byte[] getCommd(); | ||
| 9 | + String getReplyCommdTopic(Topic topic); | ||
| 10 | + void setReplyMessage(Message message); | ||
| 11 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/factory/MqttClientPersistenceImpl.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.factory; | ||
| 2 | + | ||
| 3 | +import org.eclipse.paho.client.mqttv3.MqttClientPersistence; | ||
| 4 | +import org.eclipse.paho.client.mqttv3.MqttPersistable; | ||
| 5 | +import org.eclipse.paho.client.mqttv3.MqttPersistenceException; | ||
| 6 | + | ||
| 7 | +import java.util.Enumeration; | ||
| 8 | + | ||
| 9 | +public class MqttClientPersistenceImpl implements MqttClientPersistence { | ||
| 10 | + @Override | ||
| 11 | + public void open(String clientId, String serverURI) throws MqttPersistenceException { | ||
| 12 | + | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public void close() throws MqttPersistenceException { | ||
| 17 | + | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public void put(String key, MqttPersistable persistable) throws MqttPersistenceException { | ||
| 22 | + | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public MqttPersistable get(String key) throws MqttPersistenceException { | ||
| 27 | + return null; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public void remove(String key) throws MqttPersistenceException { | ||
| 32 | + | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public Enumeration keys() throws MqttPersistenceException { | ||
| 37 | + return null; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public void clear() throws MqttPersistenceException { | ||
| 42 | + | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public boolean containsKey(String key) throws MqttPersistenceException { | ||
| 47 | + return false; | ||
| 48 | + } | ||
| 49 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.factory; | ||
| 2 | + | ||
| 3 | +import com.zhonglai.luhui.mqtt.comm.config.RedisConfig; | ||
| 4 | +import com.zhonglai.luhui.mqtt.comm.config.SysParameter; | ||
| 5 | +import com.zhonglai.luhui.mqtt.comm.dto.MyException; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.util.Optional; | ||
| 9 | + | ||
| 10 | +@Data | ||
| 11 | +public class Topic { | ||
| 12 | + private String roleid; | ||
| 13 | + private String username; | ||
| 14 | + private String clientid; | ||
| 15 | + private String topicType; | ||
| 16 | + private String redicKey; | ||
| 17 | + private String messageid; | ||
| 18 | + public Topic(String topic) | ||
| 19 | + { | ||
| 20 | + topic = Optional.ofNullable(topic).orElseThrow(()->new MyException("topic为空")); | ||
| 21 | + String[] sts = topic.split("/"); | ||
| 22 | + String[] config = SysParameter.topicconfig.split("/"); | ||
| 23 | + for(int i=0;i<config.length;i++) | ||
| 24 | + { | ||
| 25 | + String cf = config[i].replace("{{","").replace("}}",""); | ||
| 26 | + switch (cf) | ||
| 27 | + { | ||
| 28 | + case "roleid": | ||
| 29 | + roleid = sts[i]; | ||
| 30 | + break; | ||
| 31 | + case "username": | ||
| 32 | + username = sts[i]; | ||
| 33 | + break; | ||
| 34 | + case "clientid": | ||
| 35 | + clientid = sts[i]; | ||
| 36 | + break; | ||
| 37 | + case "topicType": | ||
| 38 | + topicType = sts[i]; | ||
| 39 | + break; | ||
| 40 | + case "messageid": | ||
| 41 | + messageid = sts[i]; | ||
| 42 | + break; | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + public String getRedicKey() | ||
| 47 | + { | ||
| 48 | + if(null == redicKey) | ||
| 49 | + { | ||
| 50 | + return generateRedicKey(); | ||
| 51 | + } | ||
| 52 | + return redicKey; | ||
| 53 | + } | ||
| 54 | + private String generateRedicKey() | ||
| 55 | + { | ||
| 56 | + return RedisConfig.FIELD+roleid+":"+username+":"+clientid; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto; | ||
| 5 | +import com.zhonglai.luhui.mqtt.comm.factory.Topic; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 缓存业务 | ||
| 9 | + */ | ||
| 10 | +public interface CacheService { | ||
| 11 | + boolean updateCache(Topic topic, ServerDto dto); //返回是否需要持久化 | ||
| 12 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/service/ClienNoticeService.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | +import com.ruoyi.common.core.domain.Message; | ||
| 4 | +import com.ruoyi.common.utils.ByteUtil; | ||
| 5 | +import com.zhonglai.luhui.mqtt.comm.clien.ClienConnection; | ||
| 6 | +import com.zhonglai.luhui.mqtt.comm.clien.impl.ClienConnectionImpl; | ||
| 7 | +import com.zhonglai.luhui.mqtt.comm.dto.ServerDto; | ||
| 8 | +import com.zhonglai.luhui.mqtt.comm.factory.Topic; | ||
| 9 | +import net.jodah.expiringmap.ExpirationListener; | ||
| 10 | +import net.jodah.expiringmap.ExpirationPolicy; | ||
| 11 | +import net.jodah.expiringmap.ExpiringMap; | ||
| 12 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 13 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
| 14 | +import org.slf4j.Logger; | ||
| 15 | +import org.slf4j.LoggerFactory; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.beans.factory.annotation.Value; | ||
| 18 | +import org.springframework.stereotype.Service; | ||
| 19 | + | ||
| 20 | +import javax.annotation.PostConstruct; | ||
| 21 | +import java.util.concurrent.TimeUnit; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 客户端通知服务 | ||
| 25 | + */ | ||
| 26 | +@Service | ||
| 27 | +public class ClienNoticeService { | ||
| 28 | + private static final Logger log = LoggerFactory.getLogger(ClienNoticeService.class); | ||
| 29 | + | ||
| 30 | + @Autowired | ||
| 31 | + private TerminalService terminalService; | ||
| 32 | + | ||
| 33 | + @Autowired | ||
| 34 | + private TopicsService topicsService; | ||
| 35 | + | ||
| 36 | + private ExpiringMap<String, ClienConnection> clienConnectionMap; | ||
| 37 | + | ||
| 38 | + @Value("${mqtt.client.operationTime}") | ||
| 39 | + private long operationTime; //客户端操作时间 | ||
| 40 | + | ||
| 41 | + @PostConstruct | ||
| 42 | + public void init() | ||
| 43 | + { | ||
| 44 | + // maxSize: 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间) | ||
| 45 | + // expiration:设置每个key有效时间10s, 如果key不设置过期时间,key永久有效。 | ||
| 46 | + // variableExpiration: 允许更新过期时间值,如果不设置variableExpiration,不允许后面更改过期时间,一旦执行更改过期时间操作会抛异常UnsupportedOperationException | ||
| 47 | + // policy: | ||
| 48 | + // CREATED: 只在put和replace方法清零过期时间 | ||
| 49 | + // ACCESSED: 在CREATED策略基础上增加, 在还没过期时get方法清零过期时间。 | ||
| 50 | + // 清零过期时间也就是重置过期时间,重新计算过期时间. | ||
| 51 | + clienConnectionMap = ExpiringMap.builder().maxSize(2000).expiration(operationTime, TimeUnit.SECONDS) | ||
| 52 | + .asyncExpirationListener((ExpirationListener<String, ClienConnection>) (s, clienConnection) -> clienConnection.close()) | ||
| 53 | + .expirationPolicy(ExpirationPolicy.CREATED).build(); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public Message sendMessage(String imei, MqttMessage mqttMessage, String messageid) throws MqttException, InterruptedException { | ||
| 57 | + //设置通知渠道 | ||
| 58 | + ClienConnection clienConnection = new ClienConnectionImpl(); | ||
| 59 | + clienConnectionMap.put(topicsService.getClienConnectionMapKey(imei,messageid),clienConnection); | ||
| 60 | + | ||
| 61 | + sendMessage(imei,messageid,mqttMessage); | ||
| 62 | + synchronized(clienConnection) | ||
| 63 | + { | ||
| 64 | + log.info("{}等待通知",imei); | ||
| 65 | + clienConnection.wait(operationTime*1000+3000l); | ||
| 66 | + } | ||
| 67 | + log.info("{}收到通知{}",imei,clienConnection.getReplyMessage().getMessage()); | ||
| 68 | + Message message = clienConnection.getReplyMessage(); | ||
| 69 | + log.info("{}返回通知{}",imei,message); | ||
| 70 | + | ||
| 71 | + return message; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 发送消息 | ||
| 76 | + * @param imei | ||
| 77 | + * @param mqttMessage | ||
| 78 | + * @throws MqttException | ||
| 79 | + * @throws InterruptedException | ||
| 80 | + */ | ||
| 81 | + public void sendMessage(String imei,String messageid, MqttMessage mqttMessage) throws MqttException, InterruptedException { | ||
| 82 | + //发生指令,等待通知 | ||
| 83 | + String topic = topicsService.getTopicFromImei(imei,messageid); | ||
| 84 | + System.out.println("发送的消息内容"+ ByteUtil.hexStringToSpace(ByteUtil.toHexString(mqttMessage.getPayload()).toUpperCase())); | ||
| 85 | + terminalService.publish(topic,mqttMessage); | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + public ClienConnection getClienConnection(String imei, String messageid) | ||
| 89 | + { | ||
| 90 | + return clienConnectionMap.get(topicsService.getClienConnectionMapKey(imei,messageid)); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + public void replySendMessage(Topic topic, ServerDto dto) | ||
| 94 | + { | ||
| 95 | + log.info("开始通知{},数据:{}",topic,dto); | ||
| 96 | + //判断有没有需要回复的客户端,如果有就回复 | ||
| 97 | + if(dto.isReplyMessage()) | ||
| 98 | + { | ||
| 99 | + ClienConnection clienConnection = getClienConnection(topic.getClientid(),dto.getServerAgreementContent().getClienConnectionId()); | ||
| 100 | + if(null != clienConnection) | ||
| 101 | + { | ||
| 102 | + synchronized(clienConnection) | ||
| 103 | + { | ||
| 104 | + log.info("正在通知{},通知结果{}",topic,dto); | ||
| 105 | + clienConnection.reply(dto.getServerAgreementContent()); | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | + log.info("结束通知{}",topic); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public void replyTerminalMessage(Topic topic, ServerDto dto) throws MqttException { | ||
| 113 | + if(dto.isReplyMessage() && null != dto.getServerAgreementContent().getReplyCommdTopic(topic)) | ||
| 114 | + { | ||
| 115 | + String tc = dto.getServerAgreementContent().getReplyCommdTopic(topic); | ||
| 116 | + MqttMessage mqttMessage = new MqttMessage(); | ||
| 117 | + mqttMessage.setPayload(dto.getServerAgreementContent().getCommd()); | ||
| 118 | + log.info("回复终端{}的消息{}",tc,new String(mqttMessage.getPayload())); | ||
| 119 | + terminalService.publish(dto.getServerAgreementContent().getReplyCommdTopic(topic),mqttMessage); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + } | ||
| 123 | +} |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/service/DataPersistenceService.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | +import org.apache.commons.lang3.StringUtils; | ||
| 4 | + | ||
| 5 | +import java.util.ArrayList; | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 数据持久化服务 | ||
| 10 | + */ | ||
| 11 | +public abstract class DataPersistenceService { | ||
| 12 | + protected BaseDao baseDao = new BaseDao(); | ||
| 13 | + | ||
| 14 | + public abstract void persistence(Topic topic, ServerDto serverDto); | ||
| 15 | + public abstract void addDeviceSensorData(Topic topic, ServerDto serverDto); | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 记录操作日志 | ||
| 19 | + * @param deviceOperationTypeEnum | ||
| 20 | + * @param deviceNewState | ||
| 21 | + */ | ||
| 22 | + public void logDeviceOperation(String deviceInfoId, DeviceOperationTypeEnum deviceOperationTypeEnum, String deviceNewState) | ||
| 23 | + { | ||
| 24 | + List<Object> operateHisList = new ArrayList<>(); | ||
| 25 | + //如果老的和新的不一致,记录日志 | ||
| 26 | + LogDeviceOperation operateHis = new LogDeviceOperation(); | ||
| 27 | + operateHis.setDeviceInfoId(deviceInfoId); | ||
| 28 | + deviceOperationTypeEnum.setDeviceOperationLog(operateHis); | ||
| 29 | + if(StringUtils.isNoneBlank(deviceNewState)) | ||
| 30 | + { | ||
| 31 | + operateHis.setDeviceNewState(deviceNewState); | ||
| 32 | + } | ||
| 33 | + operateHis.setIsStateChange(1); | ||
| 34 | + operateHis.setDeviceOperationTime(CommonUtil.getNowTimeMilly()); | ||
| 35 | + | ||
| 36 | + operateHisList.add(operateHis); | ||
| 37 | + baseDao.insertList(operateHisList, TableGenerateSqlEnum.LogDeviceOperation.getNowTableName()); | ||
| 38 | + | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 离线处理 | ||
| 43 | + * @param imei | ||
| 44 | + */ | ||
| 45 | + public void offLine(String imei) { | ||
| 46 | + addAlarm(upDeviceInfoOffLine(imei)); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 更新设备表离线信息 | ||
| 51 | + * @param imei | ||
| 52 | + * @return | ||
| 53 | + */ | ||
| 54 | + public abstract String[] upDeviceInfoOffLine(String imei); | ||
| 55 | + | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 离线告警记录添加(实时离线) | ||
| 59 | + * @param deviceInfoIds | ||
| 60 | + */ | ||
| 61 | + private void addAlarm(String... deviceInfoIds) | ||
| 62 | + { | ||
| 63 | + List<Object> deviceAlarmInfoList = new ArrayList<>(); | ||
| 64 | + | ||
| 65 | + for(String deviceInfoId:deviceInfoIds) | ||
| 66 | + { | ||
| 67 | + DeviceAlarmInfo deviceAlarmInfo = new DeviceAlarmInfo(); | ||
| 68 | + deviceAlarmInfo.setDeviceInfoId(deviceInfoId); | ||
| 69 | + deviceAlarmInfo.setAlarmTime(CommonUtil.getNowTimeMilly()); | ||
| 70 | + deviceAlarmInfo.setAlarmState(1); | ||
| 71 | + deviceAlarmInfo.setAlarmCode("09"); | ||
| 72 | + deviceAlarmInfo.setIsSendNumber(0); | ||
| 73 | + deviceAlarmInfoList.add(deviceAlarmInfo); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + if(null != deviceAlarmInfoList && deviceAlarmInfoList.size() !=0 ) | ||
| 77 | + { | ||
| 78 | + String tableName = TableUtil.getNowTableName( "ly_device_alarm_info", "device_alarm_info",3); | ||
| 79 | + baseDao.insertList(deviceAlarmInfoList,tableName); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | +} |
| 1 | -package com.zhonglai.luhui.mqtt.service; | 1 | +package com.zhonglai.luhui.mqtt.comm.service; |
| 2 | 2 | ||
| 3 | import com.luhui.ly.device.mqtt.comm.dto.ServerDto; | 3 | import com.luhui.ly.device.mqtt.comm.dto.ServerDto; |
| 4 | import com.luhui.ly.device.mqtt.comm.factory.BusinessAgreement; | 4 | import com.luhui.ly.device.mqtt.comm.factory.BusinessAgreement; |
lh-mqtt-service/src/main/java/com/zhonglai/luhui/mqtt/comm/service/RedisKeyExpirationListener.java
0 → 100644
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | +import com.luhui.ly.device.mqtt.comm.config.RedisConfig; | ||
| 4 | +import com.luhui.ly.device.mqtt.comm.service.DataPersistenceService; | ||
| 5 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 6 | +import org.springframework.data.redis.connection.Message; | ||
| 7 | +import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; | ||
| 8 | +import org.springframework.data.redis.listener.RedisMessageListenerContainer; | ||
| 9 | +import org.springframework.stereotype.Component; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 不指定redis数据库(全局监听) | ||
| 13 | + */ | ||
| 14 | +@Component | ||
| 15 | +public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { | ||
| 16 | + @Autowired | ||
| 17 | + private DataPersistenceService dtaPersistenceService; | ||
| 18 | + | ||
| 19 | + public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { | ||
| 20 | + super(listenerContainer); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + @Override | ||
| 24 | + public void onMessage(Message message, byte[] pattern) { | ||
| 25 | + String expiredKey = message.toString(); | ||
| 26 | + if(expiredKey.startsWith(RedisConfig.getRedisKeyPath())) //指定设备离线 | ||
| 27 | + { | ||
| 28 | + String imei = expiredKey.replace(RedisConfig.getRedisKeyPath(),""); | ||
| 29 | + dtaPersistenceService.offLine(imei); | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | +import com.luhui.ly.comm.util.GsonConstructor; | ||
| 4 | +import org.slf4j.Logger; | ||
| 5 | +import org.slf4j.LoggerFactory; | ||
| 6 | +import org.springframework.beans.factory.annotation.Value; | ||
| 7 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 8 | +import org.springframework.stereotype.Service; | ||
| 9 | +import org.springframework.util.CollectionUtils; | ||
| 10 | + | ||
| 11 | +import javax.annotation.Resource; | ||
| 12 | +import java.util.*; | ||
| 13 | +import java.util.concurrent.TimeUnit; | ||
| 14 | +import java.util.stream.Collectors; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * 缓存数据服务 | ||
| 18 | + */ | ||
| 19 | +@Service | ||
| 20 | +public class RedisService { | ||
| 21 | + private static final Logger log = LoggerFactory.getLogger(com.luhui.ly.device.mqtt.comm.service.RedisService.class); | ||
| 22 | + | ||
| 23 | + @Resource | ||
| 24 | + private RedisTemplate<String,Object> redisTemplate; | ||
| 25 | + | ||
| 26 | + @Value("${mqtt.client.device_life}") | ||
| 27 | + private long device_life; //设备生命周期 | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * @param key | ||
| 31 | + * @return 获得值 | ||
| 32 | + * redis有五种数据类型 opsForValue表示是操作字符串类型 | ||
| 33 | + */ | ||
| 34 | + public Object get(String key){ | ||
| 35 | + return key == null ? null : redisTemplate.opsForValue().get(key); | ||
| 36 | + } | ||
| 37 | + //本来只可以放入string类型,但是我们配置了自动序列化所以这儿可以传入object | ||
| 38 | + public boolean set(String key,Object value){ | ||
| 39 | + try{ | ||
| 40 | + redisTemplate.opsForValue().set(key,value); | ||
| 41 | + return true; | ||
| 42 | + }catch (Exception e){ | ||
| 43 | + log.error("redis set value exception:{}",e); | ||
| 44 | + return false; | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 原子操作 | ||
| 50 | + * @param key | ||
| 51 | + * @param value | ||
| 52 | + * @return | ||
| 53 | + */ | ||
| 54 | + public boolean setexDevice(String key,Object value){ | ||
| 55 | + try{//TimeUnit.SECONDS指定类型为秒 | ||
| 56 | + redisTemplate.opsForValue().set(key,value,device_life, TimeUnit.SECONDS); | ||
| 57 | + return true; | ||
| 58 | + }catch (Exception e){ | ||
| 59 | + log.error("redis set value and expire exception:{}",e); | ||
| 60 | + return false; | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 原子操作 | ||
| 66 | + * @param key | ||
| 67 | + * @param value | ||
| 68 | + * @param expire 过期时间 秒 | ||
| 69 | + * @return | ||
| 70 | + */ | ||
| 71 | + public boolean setex(String key,Object value,long expire){ | ||
| 72 | + try{//TimeUnit.SECONDS指定类型为秒 | ||
| 73 | + redisTemplate.opsForValue().set(key,value,expire, TimeUnit.SECONDS); | ||
| 74 | + return true; | ||
| 75 | + }catch (Exception e){ | ||
| 76 | + log.error("redis set value and expire exception:{}",e); | ||
| 77 | + return false; | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 非原子操作 | ||
| 83 | + * @param key | ||
| 84 | + * @param expire | ||
| 85 | + * @return | ||
| 86 | + */ | ||
| 87 | + public boolean expire(String key,long expire){ | ||
| 88 | + try{//这儿没有ops什么的是因为每种数据类型都能设置过期时间 | ||
| 89 | + redisTemplate.expire(key,expire,TimeUnit.SECONDS); | ||
| 90 | + return true; | ||
| 91 | + }catch (Exception e){ | ||
| 92 | + log.error("redis set key expire exception:{}",e); | ||
| 93 | + return false; | ||
| 94 | + } | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * | ||
| 99 | + * @param key | ||
| 100 | + * @return 获取key的过期时间 | ||
| 101 | + */ | ||
| 102 | + public long ttl(String key){ | ||
| 103 | + return redisTemplate.getExpire(key); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * | ||
| 108 | + * @param keys 删除key 可变参数 | ||
| 109 | + */ | ||
| 110 | + public void del(String ...keys){ | ||
| 111 | + if(keys!=null&&keys.length>0) { | ||
| 112 | + redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys)); | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * | ||
| 118 | + * @param key | ||
| 119 | + * @param step 传入正数 就是加多少 传入负数就是减多少 | ||
| 120 | + * @return | ||
| 121 | + */ | ||
| 122 | + public long incrBy(String key,long step){ | ||
| 123 | + return redisTemplate.opsForValue().increment(key,step); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + /** | ||
| 127 | + * | ||
| 128 | + * @param key | ||
| 129 | + * @param value | ||
| 130 | + * @return 如果该key存在就返回false 设置不成功 key不存在就返回ture设置成功 | ||
| 131 | + */ | ||
| 132 | + public boolean setnx(String key,Object value){ | ||
| 133 | + return redisTemplate.opsForValue().setIfAbsent(key,value); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | +// /** | ||
| 137 | +// * 原子操作 | ||
| 138 | +// * @param key | ||
| 139 | +// * @param value | ||
| 140 | +// * @param expire 在上面方法加上过期时间设置 | ||
| 141 | +// * @return | ||
| 142 | +// */ | ||
| 143 | +// public boolean setnxAndExpire(String key,Object value,long expire){ | ||
| 144 | +// return redisTemplate.opsForValue().setIfAbsent(key,value,expire,TimeUnit.SECONDS); | ||
| 145 | +// } | ||
| 146 | + | ||
| 147 | + /** | ||
| 148 | + * | ||
| 149 | + * @param key | ||
| 150 | + * @param value | ||
| 151 | + * @return 如果该key存在就返回之前的value 不存在就返回null | ||
| 152 | + */ | ||
| 153 | + public Object getAndSet(String key,Object value){ | ||
| 154 | + return redisTemplate.opsForValue().getAndSet(key,value); | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + /** | ||
| 158 | + * | ||
| 159 | + * @param key | ||
| 160 | + * @return 判断key是否存在 | ||
| 161 | + */ | ||
| 162 | + public boolean hasKey(String key){ | ||
| 163 | + return redisTemplate.hasKey(key); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + /***list的长度**/ | ||
| 167 | + public long llen(String key){ | ||
| 168 | + return redisTemplate.opsForList().size(key); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + /** | ||
| 172 | + * 获取key中index位置的值,负数就反过来数,-1为最后一个 | ||
| 173 | + * @param key | ||
| 174 | + * @param index | ||
| 175 | + * @return | ||
| 176 | + */ | ||
| 177 | + public Object lgetByIndex(String key,long index){ | ||
| 178 | + try { | ||
| 179 | + return redisTemplate.opsForList().index(key, index); | ||
| 180 | + } catch (Exception e) { | ||
| 181 | + log.error("redis lgetByIndex error,key:{},index:{}exception:{}",key,index,e); | ||
| 182 | + return null; | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + /** | ||
| 186 | + * 将list放入缓存 | ||
| 187 | + * @param key 键 | ||
| 188 | + * @param value 值 | ||
| 189 | + * @return | ||
| 190 | + */ | ||
| 191 | + public boolean lrpush(String key, Object value) { | ||
| 192 | + try { | ||
| 193 | + redisTemplate.opsForList().rightPush(key, value); | ||
| 194 | + return true; | ||
| 195 | + } catch (Exception e) { | ||
| 196 | + log.error("redis lrpush error,key:{},value:{}exception:{}",key,value,e); | ||
| 197 | + return false; | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + /** | ||
| 201 | + * 将list放入缓存 | ||
| 202 | + * @param key 键 | ||
| 203 | + * @param value 值 | ||
| 204 | + * @param time 时间(秒) | ||
| 205 | + * @return | ||
| 206 | + */ | ||
| 207 | + public boolean lrpush(String key, Object value, long time) { | ||
| 208 | + try { | ||
| 209 | + redisTemplate.opsForList().rightPush(key, value); | ||
| 210 | + if (time > 0) | ||
| 211 | + expire(key, time); | ||
| 212 | + return true; | ||
| 213 | + } catch (Exception e) { | ||
| 214 | + log.error("redis lrpush error,key:{},value:{},timeL{},exception:{}",key,value,time,e); | ||
| 215 | + return false; | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | + /** | ||
| 219 | + * 将list放入缓存 | ||
| 220 | + * @param key 键 | ||
| 221 | + * @param value 值 | ||
| 222 | + * @return | ||
| 223 | + */ | ||
| 224 | + public boolean lrpush(String key, List<Object> value) { | ||
| 225 | + try { | ||
| 226 | + redisTemplate.opsForList().rightPushAll(key, value); | ||
| 227 | + return true; | ||
| 228 | + } catch (Exception e) { | ||
| 229 | + log.error("redis lrpush error,key:{},value:{},exception:{}",key,value,e); | ||
| 230 | + return false; | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + /** | ||
| 234 | + * 将list放入缓存 | ||
| 235 | + * | ||
| 236 | + * @param key 键 | ||
| 237 | + * @param value 值 | ||
| 238 | + * @param time 时间(秒) | ||
| 239 | + * @return | ||
| 240 | + */ | ||
| 241 | + public boolean lrpush(String key, List<Object> value, long time) { | ||
| 242 | + try { | ||
| 243 | + redisTemplate.opsForList().rightPushAll(key, value); | ||
| 244 | + if (time > 0) | ||
| 245 | + expire(key, time); | ||
| 246 | + return true; | ||
| 247 | + } catch (Exception e) { | ||
| 248 | + log.error("redis lrpush error,key:{},value:{},time:{},exception:{}",key,value,time,e); | ||
| 249 | + return false; | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + /** | ||
| 253 | + * 根据索引修改list中的某条数据 | ||
| 254 | + * @param key 键 | ||
| 255 | + * @param index 索引 | ||
| 256 | + * @param value 值 | ||
| 257 | + * @return | ||
| 258 | + */ | ||
| 259 | + public boolean lUpdateByIndex(String key, long index, Object value) { | ||
| 260 | + try { | ||
| 261 | + redisTemplate.opsForList().set(key, index, value); | ||
| 262 | + return true; | ||
| 263 | + } catch (Exception e) { | ||
| 264 | + log.error("redis lUpdateByIndex error,key:{},index:{},value:{},exception:{}",key,index,value,e); | ||
| 265 | + return false; | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + /** | ||
| 269 | + * 移除N个值为value | ||
| 270 | + * @param key 键 | ||
| 271 | + * @param count 移除多少个 | ||
| 272 | + * @param value 值 | ||
| 273 | + * @return 移除的个数 | ||
| 274 | + */ | ||
| 275 | + public long lrem(String key, long count, Object value) { | ||
| 276 | + try { | ||
| 277 | + Long remove = redisTemplate.opsForList().remove(key, count, value); | ||
| 278 | + return remove; | ||
| 279 | + } catch (Exception e) { | ||
| 280 | + log.error("redis lrem error,key:{},count:{},value:{},exception:{}",key,count,value,e); | ||
| 281 | + return 0; | ||
| 282 | + } | ||
| 283 | + } | ||
| 284 | + /*****hash数据类型方法 opsForHash表示是操作字符串类型*****/ | ||
| 285 | + | ||
| 286 | + /** | ||
| 287 | + * @param key 健 | ||
| 288 | + * @param field 属性 | ||
| 289 | + * @param value 值 | ||
| 290 | + * @return | ||
| 291 | + */ | ||
| 292 | + public boolean hset(String key, String field, Object value) { | ||
| 293 | + try { | ||
| 294 | + redisTemplate.opsForHash().put(key, field, value); | ||
| 295 | + return true; | ||
| 296 | + }catch (Exception e){ | ||
| 297 | + log.error("redis hset eror,key:{},field:{},value:{}",key,field,value); | ||
| 298 | + return false; | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + | ||
| 302 | + /** | ||
| 303 | + * | ||
| 304 | + * @param key | ||
| 305 | + * @param field | ||
| 306 | + * @param value | ||
| 307 | + * @param seconds(秒) 过期时间 | ||
| 308 | + * @return | ||
| 309 | + */ | ||
| 310 | + public boolean hset(String key, String field, Object value,long seconds) { | ||
| 311 | + try { | ||
| 312 | + redisTemplate.opsForHash().put(key, field, value); | ||
| 313 | + expire(key,seconds);//调用通用方法设置过期时间 | ||
| 314 | + return true; | ||
| 315 | + }catch (Exception e){ | ||
| 316 | + log.error("redis hset and expire eror,key:{},field:{},value:{},exception:{}",key,field,value,e); | ||
| 317 | + return false; | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + | ||
| 321 | + /** | ||
| 322 | + * 获取key中field属性的值 | ||
| 323 | + * @param key | ||
| 324 | + * @param field | ||
| 325 | + * @return | ||
| 326 | + */ | ||
| 327 | + public Object hget(String key,String field){ | ||
| 328 | + return redisTemplate.opsForHash().get(key,field); | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + /** | ||
| 332 | + * 获取key中多个属性的键值对,这儿使用map来接收 | ||
| 333 | + * @param key | ||
| 334 | + * @param fields | ||
| 335 | + * @return | ||
| 336 | + */ | ||
| 337 | + public Map<String,Object> hmget(String key, String...fields){ | ||
| 338 | + Map<String,Object> map = new HashMap<>(); | ||
| 339 | + for (String field :fields){ | ||
| 340 | + map.put(field,hget(key,field)); | ||
| 341 | + } | ||
| 342 | + return map; | ||
| 343 | + } | ||
| 344 | + | ||
| 345 | + /** | ||
| 346 | + * @param key 获得该key下的所有键值对 | ||
| 347 | + * @return | ||
| 348 | + */ | ||
| 349 | + public Map<Object, Object> hmget(String key){ | ||
| 350 | + return redisTemplate.opsForHash().entries(key); | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + /** | ||
| 354 | + * @param key 获得该key下的所有键值对 | ||
| 355 | + * @return | ||
| 356 | + */ | ||
| 357 | + //map----json字符串---->对象 | ||
| 358 | + public <T>T hmgetObject(String key,Class<T> tClass){ | ||
| 359 | + Map<Object, Object> hmget = hmget(key); | ||
| 360 | + if(CollectionUtils.isEmpty(hmget)) return null; | ||
| 361 | + //查询到了 先把数据转成json字符串 | ||
| 362 | + String s = GsonConstructor.get().toJson(hmget); | ||
| 363 | + //再把json字符串转回对象 | ||
| 364 | + return GsonConstructor.get().fromJson(s,tClass); | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + /** | ||
| 368 | + * @param key 键 | ||
| 369 | + * @param map 对应多个键值 | ||
| 370 | + * @return | ||
| 371 | + */ | ||
| 372 | + public boolean hmset(String key,Map<Object,Object> map){ | ||
| 373 | + try { | ||
| 374 | + redisTemplate.opsForHash().putAll(key, map); | ||
| 375 | + return true; | ||
| 376 | + }catch (Exception e){ | ||
| 377 | + log.error("redis hmset eror,key:{},value:{},exception:{}",key,map,e); | ||
| 378 | + return false; | ||
| 379 | + } | ||
| 380 | + } | ||
| 381 | + public boolean hmset(String key,Object object){ | ||
| 382 | + try { | ||
| 383 | + String s = GsonConstructor.get().toJson(object); | ||
| 384 | + Map<String, String> map = GsonConstructor.get().fromJson(s,HashMap.class); | ||
| 385 | + redisTemplate.opsForHash().putAll(key, map); | ||
| 386 | + return true; | ||
| 387 | + }catch (Exception e){ | ||
| 388 | + log.error("redis hmset eror,key:{},object:{},exception:{}",key,object,e); | ||
| 389 | + return false; | ||
| 390 | + } | ||
| 391 | + } | ||
| 392 | + /** | ||
| 393 | + * @param key 键 | ||
| 394 | + * @param map 对应多个键值 | ||
| 395 | + * @param seconds 过期时间(秒) | ||
| 396 | + * @return | ||
| 397 | + */ | ||
| 398 | + public boolean hmset(String key,Map<String,Object> map,long seconds){ | ||
| 399 | + try { | ||
| 400 | + redisTemplate.opsForHash().putAll(key, map); | ||
| 401 | + expire(key,seconds); | ||
| 402 | + return true; | ||
| 403 | + }catch (Exception e){ | ||
| 404 | + log.error("redis hmset eror,key:{},value:{},expireTime,exception:{}",key,map,seconds,e); | ||
| 405 | + return false; | ||
| 406 | + } | ||
| 407 | + } | ||
| 408 | + | ||
| 409 | + /** | ||
| 410 | + *删除key中的属性 | ||
| 411 | + * @param key | ||
| 412 | + * @param fields | ||
| 413 | + */ | ||
| 414 | + public void hdel(String key,Object...fields){ | ||
| 415 | + redisTemplate.opsForHash().delete(key,fields); | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + /** | ||
| 419 | + * 判断key中是否存在某属性 | ||
| 420 | + * @param key | ||
| 421 | + * @param field | ||
| 422 | + * @return | ||
| 423 | + */ | ||
| 424 | + public boolean hHashKey(String key,String field){ | ||
| 425 | + return redisTemplate.opsForHash().hasKey(key,field); | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + /** | ||
| 429 | + * 对key中filed的value增加多少 如果是减少就传入负数 | ||
| 430 | + * @param key | ||
| 431 | + * @param field | ||
| 432 | + * @param step 正数增加,负数减少 | ||
| 433 | + * @return | ||
| 434 | + */ | ||
| 435 | + public double hincr(String key,String field,double step){ | ||
| 436 | + return redisTemplate.opsForHash().increment(key,field,step); | ||
| 437 | + } | ||
| 438 | + | ||
| 439 | + /** | ||
| 440 | + * key中多少个 | ||
| 441 | + * @param key | ||
| 442 | + * @return | ||
| 443 | + */ | ||
| 444 | + public long hlen(String key){ | ||
| 445 | + return redisTemplate.opsForHash().size(key); | ||
| 446 | + } | ||
| 447 | + /******其他方法用到在增加********/ | ||
| 448 | + | ||
| 449 | + /***set集合***/ | ||
| 450 | + /** | ||
| 451 | + * 获取key中所有元素 | ||
| 452 | + * @param key | ||
| 453 | + * @return | ||
| 454 | + */ | ||
| 455 | + public Set<Object> sgetAll(String key){ | ||
| 456 | + try { | ||
| 457 | + return redisTemplate.opsForSet().members(key); | ||
| 458 | + }catch (Exception e){ | ||
| 459 | + log.error("redis sgetAll error,key:{},exception:{}",key,e); | ||
| 460 | + return null; | ||
| 461 | + } | ||
| 462 | + } | ||
| 463 | + | ||
| 464 | + /** | ||
| 465 | + * 判断value是否在key中 | ||
| 466 | + * @param key | ||
| 467 | + * @param value | ||
| 468 | + * @return | ||
| 469 | + */ | ||
| 470 | + public boolean sexists(String key,Object value){ | ||
| 471 | + try { | ||
| 472 | + return redisTemplate.opsForSet().isMember(key,value); | ||
| 473 | + }catch (Exception e){ | ||
| 474 | + log.error("redis sexists error,key:{},value:{},exception:{}",key,value,e); | ||
| 475 | + return false; | ||
| 476 | + } | ||
| 477 | + } | ||
| 478 | + | ||
| 479 | + /** | ||
| 480 | + * 插入多个元素 | ||
| 481 | + * @param key | ||
| 482 | + * @param values | ||
| 483 | + * @return 成功的个数 | ||
| 484 | + */ | ||
| 485 | + public long sset(String key ,Object...values){ | ||
| 486 | + try { | ||
| 487 | + return redisTemplate.opsForSet().add(key,values); | ||
| 488 | + }catch (Exception e){ | ||
| 489 | + log.error("redis sset error,key:{},value:{},values:{},exception:{}",key,values,e); | ||
| 490 | + return 0; | ||
| 491 | + } | ||
| 492 | + } | ||
| 493 | + | ||
| 494 | + /** | ||
| 495 | + * 添加元素并设置过期时间 (非原子操作) | ||
| 496 | + * @param key | ||
| 497 | + * @param time | ||
| 498 | + * @param values | ||
| 499 | + * @return | ||
| 500 | + */ | ||
| 501 | + public long sset(String key ,long time,Object...values){ | ||
| 502 | + try { | ||
| 503 | + long count = redisTemplate.opsForSet().add(key,values); | ||
| 504 | + expire(key,time); | ||
| 505 | + return count; | ||
| 506 | + }catch (Exception e){ | ||
| 507 | + log.error("redis sset error,key:{},value:{},values:{},exception:{}",key,values,e); | ||
| 508 | + return 0; | ||
| 509 | + } | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + /** | ||
| 513 | + * 获取set的长度 | ||
| 514 | + * @param key | ||
| 515 | + * @return | ||
| 516 | + */ | ||
| 517 | + public long sgetSize(String key){ | ||
| 518 | + try { | ||
| 519 | + return redisTemplate.opsForSet().size(key); | ||
| 520 | + }catch (Exception e){ | ||
| 521 | + log.error("redis sgetSize error,key:{},exception:{}",key,e); | ||
| 522 | + return 0; | ||
| 523 | + } | ||
| 524 | + } | ||
| 525 | + /** | ||
| 526 | + * 移除值为value的 | ||
| 527 | + * @param key 键 | ||
| 528 | + * @param values 值 可以是多个 | ||
| 529 | + * @return 移除的个数 | ||
| 530 | + */ | ||
| 531 | + public long sRemove(String key, Object... values) { | ||
| 532 | + try { | ||
| 533 | + Long count = redisTemplate.opsForSet().remove(key, values); | ||
| 534 | + return count; | ||
| 535 | + } catch (Exception e) { | ||
| 536 | + log.error("redis sRemove error,key:{},values:{},exception:{}",key,values,e); | ||
| 537 | + return 0; | ||
| 538 | + } | ||
| 539 | + } | ||
| 540 | + | ||
| 541 | + /** | ||
| 542 | + * 随机取count个元素 count为正数就取不重复的 负数就有可能重复 | ||
| 543 | + * @param key | ||
| 544 | + * @param count | ||
| 545 | + * @return | ||
| 546 | + */ | ||
| 547 | + public List<Object> sRandom(String key,long count) { | ||
| 548 | + try { | ||
| 549 | + return redisTemplate.opsForSet().randomMembers(key,count); | ||
| 550 | + } catch (Exception e) { | ||
| 551 | + log.error("redis sRandom error,key:{},count:{},exception:{}",key,count,e); | ||
| 552 | + return null; | ||
| 553 | + } | ||
| 554 | + } | ||
| 555 | + /****zset工具类***/ | ||
| 556 | + /** | ||
| 557 | + * 添加元素 | ||
| 558 | + * @param key | ||
| 559 | + * @param member | ||
| 560 | + * @param score | ||
| 561 | + * @return | ||
| 562 | + */ | ||
| 563 | + public boolean zadd(String key,Object member,double score){ | ||
| 564 | + try { | ||
| 565 | + return redisTemplate.opsForZSet().add(key,member,score); | ||
| 566 | + } catch (Exception e) { | ||
| 567 | + log.error("redis zadd error,key:{},value:{},score:{},exception:{}",key,member,score,e); | ||
| 568 | + return false; | ||
| 569 | + } | ||
| 570 | + } | ||
| 571 | + public Set<String> zrange(String key,int start,int end){ | ||
| 572 | + | ||
| 573 | + try { | ||
| 574 | + Set<Object> range = redisTemplate.opsForZSet(). | ||
| 575 | + range(key, start, end); | ||
| 576 | + if(range==null||range.size()==0) return null; | ||
| 577 | + return range.stream(). | ||
| 578 | + map(o->(String)o).collect(Collectors.toSet()); | ||
| 579 | + } catch (Exception e) { | ||
| 580 | + log.error("redis zrange error,key:{},start:{},end:{},exception:{}", | ||
| 581 | + key,start,end,e); | ||
| 582 | + return null; | ||
| 583 | + } | ||
| 584 | + } | ||
| 585 | + | ||
| 586 | + /** | ||
| 587 | + * 模糊匹配key | ||
| 588 | + * @param match | ||
| 589 | + * @return | ||
| 590 | + */ | ||
| 591 | + public Set<String> keys(String match){ | ||
| 592 | + Set<String> ks = redisTemplate.keys(match); | ||
| 593 | + return ks; | ||
| 594 | + } | ||
| 595 | +/***这个有点多,就不一一写了 大家用到什么去补全剩下的API | ||
| 596 | + * Set< V > range(K key, long start, long end); 下标范围取 | ||
| 597 | + * Long remove(K key, Object… values); 删除元素 | ||
| 598 | + *Double incrementScore(K key, V value, double delta); 增加分数 | ||
| 599 | + * Long rank(K key, Object o); 当前元素的位置 | ||
| 600 | + *Long reverseRank(K key, Object o); 反过来的位置 | ||
| 601 | + *Set< TypedTuple< V >> rangeWithScores(K key, long start, long end); 下标取出来带分数 | ||
| 602 | + * Set< V > rangeByScore(K key, double min, double max); 根据分数取 | ||
| 603 | + * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max); 根据分数取 并携带分数 | ||
| 604 | + * Set< TypedTuple< V >> rangeByScoreWithScores(K key, double min, double max, long offset, long count); 翻页 | ||
| 605 | + *Long count(K key, double min, double max); 统计分数段的个数 | ||
| 606 | + *Long size(K key); 个数 底层就是card | ||
| 607 | + * Long zCard(K key); 个数 | ||
| 608 | + * Double score(K key, Object o); 查看某个元素的分数 | ||
| 609 | + * Long removeRange(K key, long start, long end); 根据下标删除某个范围 | ||
| 610 | + * Long removeRangeByScore(K key, double min, double max); 删除某个分数段 | ||
| 611 | + * 等等 | ||
| 612 | + * **/ | ||
| 613 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.service; | ||
| 2 | + | ||
| 3 | +import com.luhui.ly.device.mqtt.comm.config.SysParameter; | ||
| 4 | +import com.luhui.ly.device.mqtt.comm.service.MqttCallback; | ||
| 5 | +import org.eclipse.paho.client.mqttv3.MqttClient; | ||
| 6 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
| 7 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
| 8 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
| 9 | +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; | ||
| 10 | +import org.slf4j.Logger; | ||
| 11 | +import org.slf4j.LoggerFactory; | ||
| 12 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 13 | +import org.springframework.beans.factory.annotation.Value; | ||
| 14 | +import org.springframework.stereotype.Service; | ||
| 15 | + | ||
| 16 | +import javax.annotation.PostConstruct; | ||
| 17 | +import java.nio.ByteBuffer; | ||
| 18 | +import java.nio.charset.Charset; | ||
| 19 | +import java.util.concurrent.ExecutorService; | ||
| 20 | +import java.util.concurrent.LinkedBlockingQueue; | ||
| 21 | +import java.util.concurrent.ThreadPoolExecutor; | ||
| 22 | +import java.util.concurrent.TimeUnit; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * 终端服务 | ||
| 26 | + */ | ||
| 27 | +@Service | ||
| 28 | +public class TerminalService { | ||
| 29 | + private static final Logger log = LoggerFactory.getLogger(com.luhui.ly.device.mqtt.comm.service.TerminalService.class); | ||
| 30 | + @Autowired | ||
| 31 | + private MqttCallback mqttCallback; | ||
| 32 | + | ||
| 33 | + @Autowired | ||
| 34 | + private SysParameter sysParameter; | ||
| 35 | + | ||
| 36 | + //业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。 | ||
| 37 | + private final static ExecutorService executorService = new ThreadPoolExecutor( | ||
| 38 | + Runtime.getRuntime().availableProcessors(), | ||
| 39 | + Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS, | ||
| 40 | + new LinkedBlockingQueue<>(50000)); | ||
| 41 | + | ||
| 42 | + @Value("${mqtt.broker}") | ||
| 43 | + private String broker; | ||
| 44 | + @Value("${mqtt.clientId}") | ||
| 45 | + private String clientId; | ||
| 46 | + @Value("${mqtt.topics}") | ||
| 47 | + private String topics; | ||
| 48 | + @Value("${mqtt.username}") | ||
| 49 | + private String username; | ||
| 50 | + @Value("${mqtt.password}") | ||
| 51 | + private String password; | ||
| 52 | + | ||
| 53 | + private MqttClient mqttclient; | ||
| 54 | + | ||
| 55 | + private MqttConnectOptions options; | ||
| 56 | + | ||
| 57 | + private void init() throws MqttException { | ||
| 58 | + if(null == mqttclient) | ||
| 59 | + { | ||
| 60 | + mqttclient = new MqttClient(broker, clientId, new MemoryPersistence()); | ||
| 61 | + } | ||
| 62 | + options = new MqttConnectOptions(); | ||
| 63 | + options.setCleanSession(true); | ||
| 64 | + options.setConnectionTimeout(15); | ||
| 65 | + //设置断开后重新连接 | ||
| 66 | + options.setAutomaticReconnect(true); | ||
| 67 | + mqttclient.setCallback(mqttCallback); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + private void connect() throws MqttException { | ||
| 71 | + options.setUserName(username); | ||
| 72 | + options.setPassword(password.toCharArray()); | ||
| 73 | + mqttclient.connect(options); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + public void subscribe() throws MqttException { | ||
| 77 | + mqttclient.subscribe(topics.split(",")); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @PostConstruct | ||
| 81 | + public void startMqttListenerService() throws MqttException { | ||
| 82 | + log.info("-----------开始启动mqtt监听服务--------------------"); | ||
| 83 | + init(); | ||
| 84 | + log.info("-----------启动参数{}--------------------",options); | ||
| 85 | + connect(); | ||
| 86 | + subscribe(); | ||
| 87 | + sysParameter.inittopicconfig(); | ||
| 88 | + log.info("-----------mqtt监听服务启动成功--------------------"); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + public void subscribe(String[] topicFilters) throws MqttException { | ||
| 92 | + mqttclient.subscribe(topicFilters); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + public void publish(String topic, MqttMessage message) throws MqttException { | ||
| 96 | + mqttclient.publish(topic,message); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + public void publish(String topic, String messageStr) throws MqttException { | ||
| 100 | + MqttMessage message = new MqttMessage(); | ||
| 101 | + message.setPayload(messageStr.getBytes()); | ||
| 102 | + mqttclient.publish(topic,message); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + public void closeClient (String clientId,String code,String messageStr) throws MqttException { | ||
| 106 | + String topic = "SYSOPERATION/CLOSE"; | ||
| 107 | + MqttMessage message = new MqttMessage(); | ||
| 108 | + Charset charset = Charset.forName("utf-8"); | ||
| 109 | + ByteBuffer payload = charset.encode(clientId+","+code+","+messageStr); | ||
| 110 | + message.setPayload(payload.array()); | ||
| 111 | + mqttclient.publish(topic,message); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | +} |
| 1 | +package com.zhonglai.luhui.mqtt.comm.util; | ||
| 2 | + | ||
| 3 | +import com.google.gson.JsonObject; | ||
| 4 | +import org.apache.commons.lang3.StringUtils; | ||
| 5 | + | ||
| 6 | +public class ChangUtil { | ||
| 7 | + public static boolean changString(JsonObject oldData, String strName, String strValue) | ||
| 8 | + { | ||
| 9 | + if(StringUtils.isNoneBlank(strValue)) | ||
| 10 | + { | ||
| 11 | + if(!oldData.has(strName) || !strValue.equals(oldData.get(strName).getAsString())) | ||
| 12 | + { | ||
| 13 | + oldData.addProperty(strName,strValue); | ||
| 14 | + return true; | ||
| 15 | + } | ||
| 16 | + } | ||
| 17 | + return false; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public static boolean changInt(JsonObject oldData, String intName, Integer intValue) | ||
| 21 | + { | ||
| 22 | + if(null != intValue) | ||
| 23 | + { | ||
| 24 | + if(!oldData.has(intName) || intValue -oldData.get(intName).getAsInt() != 0 ) | ||
| 25 | + { | ||
| 26 | + oldData.addProperty(intName,intValue); | ||
| 27 | + return true; | ||
| 28 | + } | ||
| 29 | + } | ||
| 30 | + return false; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public static boolean changFloat(JsonObject oldData, String intName, Float intValue) | ||
| 34 | + { | ||
| 35 | + if(null != intValue) | ||
| 36 | + { | ||
| 37 | + if(!oldData.has(intName) || intValue -oldData.get(intName).getAsFloat() != 0 ) | ||
| 38 | + { | ||
| 39 | + oldData.addProperty(intName,intValue); | ||
| 40 | + return true; | ||
| 41 | + } | ||
| 42 | + } | ||
| 43 | + return false; | ||
| 44 | + } | ||
| 45 | +} | ||
| 46 | + |
| @@ -289,6 +289,24 @@ | @@ -289,6 +289,24 @@ | ||
| 289 | <artifactId>mapper-spring-boot-starter</artifactId> | 289 | <artifactId>mapper-spring-boot-starter</artifactId> |
| 290 | <version>${tkmapper.version}</version> | 290 | <version>${tkmapper.version}</version> |
| 291 | </dependency> | 291 | </dependency> |
| 292 | + | ||
| 293 | + <!-- mqtt --> | ||
| 294 | + <dependency> | ||
| 295 | + <groupId>org.eclipse.paho</groupId> | ||
| 296 | + <artifactId>org.eclipse.paho.client.mqttv3</artifactId> | ||
| 297 | + <version>1.0.2</version> | ||
| 298 | + <exclusions> | ||
| 299 | + <exclusion> | ||
| 300 | + <groupId>org.slf4j</groupId> | ||
| 301 | + <artifactId>slf4j-log4j12</artifactId> | ||
| 302 | + </exclusion> | ||
| 303 | + </exclusions> | ||
| 304 | + </dependency> | ||
| 305 | + <dependency> | ||
| 306 | + <groupId>net.jodah</groupId> | ||
| 307 | + <artifactId>expiringmap</artifactId> | ||
| 308 | + <version>0.5.8</version> | ||
| 309 | + </dependency> | ||
| 292 | </dependencies> | 310 | </dependencies> |
| 293 | 311 | ||
| 294 | 312 |
| 1 | -package com.ruoyi.system.dto; | 1 | +package com.ruoyi.common.core.domain; |
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | 4 | ||
| @@ -37,4 +37,13 @@ public class Message { | @@ -37,4 +37,13 @@ public class Message { | ||
| 37 | this.code = code.getCode(); | 37 | this.code = code.getCode(); |
| 38 | this.message = code.getMessage(); | 38 | this.message = code.getMessage(); |
| 39 | } | 39 | } |
| 40 | + | ||
| 41 | + public void setCode(MessageCode messageCode ) | ||
| 42 | + { | ||
| 43 | + code = messageCode.code; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public void setCode(MessageCodeType code) { | ||
| 47 | + this.code = code.getCode(); | ||
| 48 | + } | ||
| 40 | } | 49 | } |
| 1 | +package com.ruoyi.common.utils; | ||
| 2 | + | ||
| 3 | +import java.util.Arrays; | ||
| 4 | + | ||
| 5 | +public class ByteUtil { | ||
| 6 | + /** | ||
| 7 | + * byte数组中取int数值,本方法适用于(低位在前,高位在后)的顺序,和和intToBytes()配套使用 | ||
| 8 | + * | ||
| 9 | + * @param src | ||
| 10 | + * byte数组 | ||
| 11 | + * @param offset | ||
| 12 | + * 从数组的第offset位开始 | ||
| 13 | + * @return int数值 | ||
| 14 | + */ | ||
| 15 | + public static long bytesToLongASC(byte[] src, int offset,int lenth) { | ||
| 16 | + int value = 0; | ||
| 17 | + for(int i=0;i<lenth;i++) | ||
| 18 | + { | ||
| 19 | + value = value | ((src[offset+i] & 0xFF)<<(8*i)); | ||
| 20 | + } | ||
| 21 | + return value; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 把16进制字符串转换成字节数组 | ||
| 26 | + * | ||
| 27 | + * @param hex | ||
| 28 | + * @return | ||
| 29 | + */ | ||
| 30 | + public static byte[] hexStringToByte(String hex) { | ||
| 31 | + int len = (hex.length() / 2); | ||
| 32 | + byte[] result = new byte[len]; | ||
| 33 | + char[] achar = hex.toCharArray(); | ||
| 34 | + for (int i = 0; i < len; i++) { | ||
| 35 | + int pos = i * 2; | ||
| 36 | + result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1])); | ||
| 37 | + } | ||
| 38 | + return result; | ||
| 39 | + } | ||
| 40 | + private static byte toByte(char c) { | ||
| 41 | + byte b = (byte) "0123456789ABCDEF".indexOf(c); | ||
| 42 | + return b; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 把16进制字符串转换成字节数组 | ||
| 47 | + * | ||
| 48 | + * @param hex | ||
| 49 | + * @return | ||
| 50 | + */ | ||
| 51 | + public static String hexStringToSpace(String hex) { | ||
| 52 | + if (null == hex) { | ||
| 53 | + return null; | ||
| 54 | + } else { | ||
| 55 | + StringBuilder sb = new StringBuilder(hex.length() << 1); | ||
| 56 | + | ||
| 57 | + for(int i = 0; i < hex.length(); i+=2) { | ||
| 58 | + sb.append(hex.substring(i,i+2)).append(" "); | ||
| 59 | + } | ||
| 60 | + return sb.toString(); | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 把原数组加点目标数组后面 | ||
| 66 | + * @param dest 目标数组 | ||
| 67 | + * @param src 原数组 | ||
| 68 | + * @return | ||
| 69 | + */ | ||
| 70 | + public static byte[] addBytes(byte[] dest,byte[] src ) | ||
| 71 | + { | ||
| 72 | + int dl = dest.length; | ||
| 73 | + int sl = src.length; | ||
| 74 | + dest = Arrays.copyOf(dest, dl+sl);//数组扩容 | ||
| 75 | + System.arraycopy(src,0,dest,dl,src.length); | ||
| 76 | + return dest; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 将int数值转换为占四个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 和bytesToInt2()配套使用 | ||
| 81 | + */ | ||
| 82 | + public static byte[] intToBytesDESC(long value,int lenth) | ||
| 83 | + { | ||
| 84 | + byte[] src = new byte[lenth]; | ||
| 85 | + for(int i=0;i<lenth;i++) | ||
| 86 | + { | ||
| 87 | + src[i] = (byte) ((value>>(8*(lenth-i-1))) & 0xFF); | ||
| 88 | + } | ||
| 89 | + return src; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * 将int数值转换为占四个字节的byte数组,本方法适用于(低位在前,高位在后)的顺序。 和bytesToInt()配套使用 | ||
| 94 | + * @param value | ||
| 95 | + * 要转换的int值 | ||
| 96 | + * @return byte数组 | ||
| 97 | + */ | ||
| 98 | + public static byte[] intToBytesASC( long value,int lenth) | ||
| 99 | + { | ||
| 100 | + byte[] src = new byte[lenth]; | ||
| 101 | + for(int i=lenth;i>0;i--) | ||
| 102 | + { | ||
| 103 | + src[i-1] = (byte) ((value>>(8*(i-1))) & 0xFF); | ||
| 104 | + } | ||
| 105 | + return src; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + public static void main(String[] args) { | ||
| 109 | + System.out.println(ByteUtil.toHexString( ByteUtil.intToBytesASC(2011239256,4))); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + /** | ||
| 113 | + * ip转化位4byte | ||
| 114 | + * @param ip | ||
| 115 | + * @return | ||
| 116 | + */ | ||
| 117 | + public static byte[] ipTo4Byte(String ip) | ||
| 118 | + { | ||
| 119 | + String[] ips = ip.split("."); | ||
| 120 | + return new byte[]{(byte) Integer.parseInt(ips[0]),(byte) Integer.parseInt(ips[1]),(byte) Integer.parseInt(ips[2]),(byte) Integer.parseInt(ips[3])}; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * byte数组中取int数值,本方法适用于(低位在后,高位在前)的顺序。和intToBytes2()配套使用 | ||
| 125 | + */ | ||
| 126 | + public static long bytesToLongDESC(byte[] src, int offset,int lenth) { | ||
| 127 | + long value = 0; | ||
| 128 | + for(int i=lenth;i>0;i--) | ||
| 129 | + { | ||
| 130 | + value = value | ((src[offset+(lenth-i)] & 0xFF)<<(8*(i-1))); | ||
| 131 | + } | ||
| 132 | + return value; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + private static final char[] hex = "0123456789abcdef".toCharArray(); | ||
| 136 | + public static String toHexString(byte[] bytes) { | ||
| 137 | + if (null == bytes) { | ||
| 138 | + return null; | ||
| 139 | + } else { | ||
| 140 | + StringBuilder sb = new StringBuilder(bytes.length << 1); | ||
| 141 | + | ||
| 142 | + for(int i = 0; i < bytes.length; ++i) { | ||
| 143 | + sb.append(hex[(bytes[i] & 240) >> 4]).append(hex[bytes[i] & 15]); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + return sb.toString(); | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + /** | ||
| 151 | + * 计算CRC16/Modbus校验码 低位在前,高位在后 | ||
| 152 | + * | ||
| 153 | + * @param str 十六进制字符串 | ||
| 154 | + * @return | ||
| 155 | + */ | ||
| 156 | + public static String getCRC16(String str) { | ||
| 157 | + byte[] bytes = hexStringToByte(str); | ||
| 158 | + return getCRC16(bytes); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + /** | ||
| 162 | + * 计算CRC16/Modbus校验码 低位在前,高位在后 | ||
| 163 | + * | ||
| 164 | + * @return | ||
| 165 | + */ | ||
| 166 | + public static String getCRC16( byte[] bytes) { | ||
| 167 | + int CRC = 0x0000ffff; | ||
| 168 | + int POLYNOMIAL = 0x0000a001; | ||
| 169 | + | ||
| 170 | + int i, j; | ||
| 171 | + for (i = 0; i < bytes.length; i++) { | ||
| 172 | + CRC ^= ((int) bytes[i] & 0x000000ff); | ||
| 173 | + for (j = 0; j < 8; j++) { | ||
| 174 | + if ((CRC & 0x00000001) != 0) { | ||
| 175 | + CRC >>= 1; | ||
| 176 | + CRC ^= POLYNOMIAL; | ||
| 177 | + } else { | ||
| 178 | + CRC >>= 1; | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + String crc = Integer.toHexString(CRC); | ||
| 183 | + if (crc.length() == 2) { | ||
| 184 | + crc = "00" + crc; | ||
| 185 | + } else if (crc.length() == 3) { | ||
| 186 | + crc = "0" + crc; | ||
| 187 | + } | ||
| 188 | + crc = crc.substring(2, 4) + crc.substring(0, 2); | ||
| 189 | + return crc.toUpperCase(); | ||
| 190 | + } | ||
| 191 | +} |
| @@ -47,8 +47,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | @@ -47,8 +47,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" | ||
| 47 | update iot_role | 47 | update iot_role |
| 48 | <trim prefix="SET" suffixOverrides=","> | 48 | <trim prefix="SET" suffixOverrides=","> |
| 49 | <if test="create_time != null">create_time = #{create_time},</if> | 49 | <if test="create_time != null">create_time = #{create_time},</if> |
| 50 | - <if test="describe != null">describe = #{describe},</if> | ||
| 51 | - <if test="name != null">name = #{name},</if> | 50 | + <if test="describe != null">`describe` = #{describe},</if> |
| 51 | + <if test="name != null">`name` = #{name},</if> | ||
| 52 | <if test="used != null">used = #{used},</if> | 52 | <if test="used != null">used = #{used},</if> |
| 53 | </trim> | 53 | </trim> |
| 54 | where id = #{id} | 54 | where id = #{id} |
-
请 注册 或 登录 后发表评论