refactor(ops,iot): 重构区域设备关联模块并添加Redis缓存
主要变更:
1. 将 ops_area_device_relation 表所有权移至 Ops 模块
- 新增 OpsAreaDeviceRelationDO、Mapper、Service、Controller
- 新增 AreaDeviceApi Feign 接口供其他模块调用
- ���除 IoT 模块中的旧 DO 和 Mapper
2. 实现 Redis JSON 缓存(IoT 可读)
- 统一缓存 Key: ops:area:device:{deviceId}
- 统一缓存 Key: ops:area:{areaId}:type:{relationType}
- TTL: 30分钟,空值缓存: 1分钟
3. IoT 模块通过 Feign 调用 Ops
- 优先读 Redis 缓存,未命中时调用 Ops API
- 缓存由 Ops 模块统一管理
4. 删除 IoT 模块废弃文件
- OpsAreaDeviceRelationDO.java
- OpsAreaDeviceRelationMapper.java
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,184 +1,189 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>viewsh-module-iot</artifactId>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>viewsh-module-iot-server</artifactId>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
物联网 模块,主要实现 产品管理、设备管理、协议管理等功能。
|
||||
<!-- TODO 芋艿:后续补充下 -->
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-env</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-system-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-iot-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-iot-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-rpc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO @芋艿:引入下,看看情况 -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>2.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<!-- TODO @芋艿:临时打开 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- IoT 网络组件:接收来自设备的上行数据 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.viewsh</groupId>-->
|
||||
<!-- <artifactId>viewsh-module-iot-net-component-http</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.viewsh</groupId>-->
|
||||
<!-- <artifactId>viewsh-module-iot-net-component-emqx</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>viewsh-module-iot</artifactId>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>viewsh-module-iot-server</artifactId>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
物联网 模块,主要实现 产品管理、设备管理、协议管理等功能。
|
||||
<!-- TODO 芋艿:后续补充下 -->
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-env</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-system-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-ops-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-iot-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-module-iot-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.taosdata.jdbc</groupId>
|
||||
<artifactId>taos-jdbcdriver</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-rpc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- TODO @芋艿:引入下,看看情况 -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>2.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.viewsh</groupId>
|
||||
<artifactId>viewsh-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<!-- TODO @芋艿:临时打开 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.kafka</groupId>
|
||||
<artifactId>spring-kafka</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- IoT 网络组件:接收来自设备的上行数据 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.viewsh</groupId>-->
|
||||
<!-- <artifactId>viewsh-module-iot-net-component-http</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.viewsh</groupId>-->
|
||||
<!-- <artifactId>viewsh-module-iot-net-component-emqx</artifactId>-->
|
||||
<!-- <version>${revision}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
||||
* 保洁工单集成配置
|
||||
* <p>
|
||||
* 这是 IoT 设备与保洁工单集成的总配置类,包含所有子配置
|
||||
* 存储在 {@link OpsAreaDeviceRelationDO#getConfigData()} 中
|
||||
* 存储在 Ops 模块的 {@code ops_area_device_relation.config_data} 字段中
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package com.viewsh.module.iot.dal.dataobject.integration.clean;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.viewsh.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 运营区域设备关联 DO
|
||||
* <p>
|
||||
* 用于建立运营区域与 IoT 设备的绑定关系,并存储核心检测配置
|
||||
* 注意:此表在 ops 库中创建,但在 IoT 模块中<E59D97><E4B8AD>问(通过 Feign 或直接访问)
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
@TableName(value = "ops_area_device_relation", autoResultMap = true)
|
||||
@KeySequence("ops_area_device_relation_seq")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OpsAreaDeviceRelationDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 运营区域ID
|
||||
* <p>
|
||||
* 关联 ops_bus_area.id
|
||||
*/
|
||||
private Long areaId;
|
||||
|
||||
/**
|
||||
* IoT设备ID
|
||||
* <p>
|
||||
* 关联 iot_device.id
|
||||
*/
|
||||
private Long deviceId;
|
||||
|
||||
/**
|
||||
* 设备Key(冗余字段,便于查询)
|
||||
*/
|
||||
private String deviceKey;
|
||||
|
||||
/**
|
||||
* 产品ID
|
||||
* <p>
|
||||
* 关联 iot_product.id
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 产品Key(冗余字段,便于查询)
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 关联类型
|
||||
* <p>
|
||||
* TRAFFIC_COUNTER - 客流计数器
|
||||
* BEACON - 蓝牙信标
|
||||
* BADGE - 工牌设备
|
||||
*/
|
||||
private String relationType;
|
||||
|
||||
/**
|
||||
* 配置数据(JSON格式)
|
||||
* <p>
|
||||
* 存储保洁工单集成的各类配置
|
||||
* 使用 {@link JacksonTypeHandler} 自动序列化/反序列化
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private CleanOrderIntegrationConfig configData;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
* <p>
|
||||
* true - 启用
|
||||
* false - 禁用
|
||||
*/
|
||||
private Boolean enabled;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.viewsh.module.iot.dal.mysql.integration.clean;
|
||||
|
||||
import com.viewsh.framework.common.pojo.PageResult;
|
||||
import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.iot.dal.dataobject.integration.clean.OpsAreaDeviceRelationDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 运营区域设备关联 Mapper
|
||||
*
|
||||
* @author AI
|
||||
*/
|
||||
@Mapper
|
||||
public interface OpsAreaDeviceRelationMapper extends BaseMapperX<OpsAreaDeviceRelationDO> {
|
||||
|
||||
/**
|
||||
* 根据设备ID查询关联关系(仅查询启用的)
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @return 关联关系
|
||||
*/
|
||||
default OpsAreaDeviceRelationDO selectByDeviceId(Long deviceId) {
|
||||
return selectOne(new LambdaQueryWrapperX<OpsAreaDeviceRelationDO>()
|
||||
.eq(OpsAreaDeviceRelationDO::getDeviceId, deviceId)
|
||||
.eq(OpsAreaDeviceRelationDO::getEnabled, true)
|
||||
.orderByDesc(OpsAreaDeviceRelationDO::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据区域ID查询所有启用的关联关系
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @return 关联关系列表
|
||||
*/
|
||||
default List<OpsAreaDeviceRelationDO> selectListByAreaId(Long areaId) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsAreaDeviceRelationDO>()
|
||||
.eq(OpsAreaDeviceRelationDO::getAreaId, areaId)
|
||||
.eq(OpsAreaDeviceRelationDO::getEnabled, true)
|
||||
.orderByDesc(OpsAreaDeviceRelationDO::getCreateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据区域ID和关联类型查询关联关系
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @param relationType 关联类型(TRAFFIC_COUNTER/BEACON/BADGE)
|
||||
* @return 关联关系列表
|
||||
*/
|
||||
default List<OpsAreaDeviceRelationDO> selectListByAreaIdAndRelationType(Long areaId, String relationType) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsAreaDeviceRelationDO>()
|
||||
.eq(OpsAreaDeviceRelationDO::getAreaId, areaId)
|
||||
.eq(OpsAreaDeviceRelationDO::getRelationType, relationType)
|
||||
.eq(OpsAreaDeviceRelationDO::getEnabled, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据产品Key查询所有关联关系
|
||||
*
|
||||
* @param productKey 产品Key
|
||||
* @return 关联关系列表
|
||||
*/
|
||||
default List<OpsAreaDeviceRelationDO> selectListByProductKey(String productKey) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsAreaDeviceRelationDO>()
|
||||
.eq(OpsAreaDeviceRelationDO::getProductKey, productKey)
|
||||
.eq(OpsAreaDeviceRelationDO::getEnabled, true));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
package com.viewsh.module.iot.service.integration.clean;
|
||||
|
||||
import com.viewsh.framework.common.pojo.CommonResult;
|
||||
import com.viewsh.framework.common.util.json.JsonUtils;
|
||||
import com.viewsh.module.iot.dal.dataobject.integration.clean.OpsAreaDeviceRelationDO;
|
||||
import com.viewsh.module.iot.dal.mysql.integration.clean.OpsAreaDeviceRelationMapper;
|
||||
import com.viewsh.module.iot.dal.dataobject.integration.clean.CleanOrderIntegrationConfig;
|
||||
import com.viewsh.module.ops.api.area.AreaDeviceApi;
|
||||
import com.viewsh.module.ops.api.area.AreaDeviceDTO;
|
||||
import com.viewsh.module.ops.api.area.DeviceRelationDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -22,25 +24,17 @@ import java.util.stream.Collectors;
|
||||
public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegrationConfigService {
|
||||
|
||||
/**
|
||||
* 配置缓存 Key 模式
|
||||
* Redis Key 前缀(与 Ops 模块保持一致)
|
||||
*/
|
||||
private static final String CONFIG_WRAPPER_KEY_PATTERN = "iot:clean:config:wrapper:%s";
|
||||
private static final String CONFIG_AREA_TYPE_KEY_PATTERN = "iot:clean:config:area:%s:type:%s";
|
||||
private static final String DEVICE_CACHE_KEY_PREFIX = "ops:area:device:";
|
||||
private static final String AREA_TYPE_CACHE_KEY_PREFIX = "ops:area:%s:type:%s";
|
||||
private static final String NULL_CACHE = "NULL";
|
||||
|
||||
/**
|
||||
* 配置缓存 TTL(秒)
|
||||
* <p>
|
||||
* 5 分钟自动过期
|
||||
* Ops 模块区域设备 API Client
|
||||
*/
|
||||
private static final int CONFIG_CACHE_TTL_SECONDS = 300;
|
||||
|
||||
/**
|
||||
* 空值缓存标记(防止缓存穿透)
|
||||
*/
|
||||
private static final String NULL_CACHE_VALUE = "NULL";
|
||||
|
||||
@Resource
|
||||
private OpsAreaDeviceRelationMapper relationMapper;
|
||||
private AreaDeviceApi areaDeviceApi;
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
@@ -49,11 +43,16 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra
|
||||
public List<AreaDeviceConfigWrapper> getConfigsByAreaId(Long areaId) {
|
||||
log.debug("[CleanOrderConfig] 查询区域配置:areaId={}", areaId);
|
||||
|
||||
// 区域配置暂不缓存,直接从数据库查询
|
||||
List<OpsAreaDeviceRelationDO> relations = relationMapper.selectListByAreaId(areaId);
|
||||
// 通过 Feign 调用 Ops 模块获取所有关联设备
|
||||
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, null);
|
||||
|
||||
return relations.stream()
|
||||
.map(this::wrapConfig)
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}", areaId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return result.getData().stream()
|
||||
.map(this::wrapDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -61,10 +60,16 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra
|
||||
public List<AreaDeviceConfigWrapper> getConfigsByAreaIdAndRelationType(Long areaId, String relationType) {
|
||||
log.debug("[CleanOrderConfig] 查询区域配置:areaId={}, relationType={}", areaId, relationType);
|
||||
|
||||
List<OpsAreaDeviceRelationDO> relations = relationMapper.selectListByAreaIdAndRelationType(areaId, relationType);
|
||||
// 通过 Feign 调用 Ops 模块获取关联设备
|
||||
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
|
||||
|
||||
return relations.stream()
|
||||
.map(this::wrapConfig)
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return result.getData().stream()
|
||||
.map(this::wrapDto)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -72,132 +77,133 @@ public class CleanOrderIntegrationConfigServiceImpl implements CleanOrderIntegra
|
||||
public AreaDeviceConfigWrapper getConfigByAreaIdAndRelationType(Long areaId, String relationType) {
|
||||
log.debug("[CleanOrderConfig] 查询单个区域配置:areaId={}, relationType={}", areaId, relationType);
|
||||
|
||||
// 1. 尝试从缓存读取
|
||||
String cacheKey = formatAreaTypeKey(areaId, relationType);
|
||||
String cachedValue = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
|
||||
if (cachedValue != null) {
|
||||
if (NULL_CACHE_VALUE.equals(cachedValue)) {
|
||||
log.debug("[CleanOrderConfig] 命中空值缓存:areaId={}, relationType={}", areaId, relationType);
|
||||
return null;
|
||||
// 1. 先读 Redis 缓存
|
||||
String cacheKey = String.format(AREA_TYPE_CACHE_KEY_PREFIX, areaId, relationType);
|
||||
try {
|
||||
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
if (cached != null) {
|
||||
if (NULL_CACHE.equals(cached)) {
|
||||
log.debug("[CleanOrderConfig] 命中空值缓存:areaId={}, relationType={}", areaId, relationType);
|
||||
return null;
|
||||
}
|
||||
log.debug("[CleanOrderConfig] 命中 Redis 缓存:areaId={}, relationType={}", areaId, relationType);
|
||||
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
|
||||
return wrapDto(dto);
|
||||
}
|
||||
log.debug("[CleanOrderConfig] 命中区域类型缓存:areaId={}, relationType={}", areaId, relationType);
|
||||
return JsonUtils.parseObject(cachedValue, AreaDeviceConfigWrapper.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败:areaId={}, relationType={}", areaId, relationType, e);
|
||||
}
|
||||
|
||||
// 2. 从数据库查询
|
||||
List<OpsAreaDeviceRelationDO> relations = relationMapper.selectListByAreaIdAndRelationType(areaId, relationType);
|
||||
// 2. 缓存未命中,调用 Ops 模块
|
||||
CommonResult<List<AreaDeviceDTO>> result = areaDeviceApi.getDevicesByAreaAndType(areaId, relationType);
|
||||
|
||||
if (relations.isEmpty()) {
|
||||
// 缓存空值,防止缓存穿透
|
||||
stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
log.warn("[CleanOrderConfig] 调用 Ops 模块获取区域配置失败:areaId={}, relationType={}", areaId, relationType);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 返回第一个启用的配置
|
||||
AreaDeviceConfigWrapper wrapper = relations.stream()
|
||||
.filter(r -> r.getEnabled())
|
||||
return result.getData().stream()
|
||||
.filter(dto -> dto.getEnabled() != null && dto.getEnabled())
|
||||
.findFirst()
|
||||
.map(this::wrapConfig)
|
||||
.map(this::wrapDto)
|
||||
.orElse(null);
|
||||
|
||||
// 3. 写入缓存
|
||||
if (wrapper != null) {
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
cacheKey,
|
||||
JsonUtils.toJsonString(wrapper),
|
||||
CONFIG_CACHE_TTL_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
} else {
|
||||
// 缓存空值
|
||||
stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AreaDeviceConfigWrapper getConfigWrapperByDeviceId(Long deviceId) {
|
||||
log.debug("[CleanOrderConfig] 查询设备完整配置:deviceId={}", deviceId);
|
||||
|
||||
// 1. 尝试从缓存读取
|
||||
String cacheKey = formatWrapperKey(deviceId);
|
||||
String cachedValue = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
|
||||
if (cachedValue != null) {
|
||||
if (NULL_CACHE_VALUE.equals(cachedValue)) {
|
||||
log.debug("[CleanOrderConfig] 命中空值缓存:deviceId={}", deviceId);
|
||||
return null;
|
||||
// 1. 先读 Redis 缓存
|
||||
String cacheKey = DEVICE_CACHE_KEY_PREFIX + deviceId;
|
||||
try {
|
||||
String cached = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
if (cached != null) {
|
||||
if (NULL_CACHE.equals(cached)) {
|
||||
log.debug("[CleanOrderConfig] 命中空值缓存:deviceId={}", deviceId);
|
||||
return null;
|
||||
}
|
||||
log.debug("[CleanOrderConfig] 命中 Redis 缓存:deviceId={}", deviceId);
|
||||
AreaDeviceDTO dto = JsonUtils.parseObject(cached, AreaDeviceDTO.class);
|
||||
return wrapDto(dto);
|
||||
}
|
||||
log.debug("[CleanOrderConfig] 命中 Wrapper 缓存:deviceId={}", deviceId);
|
||||
return JsonUtils.parseObject(cachedValue, AreaDeviceConfigWrapper.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("[CleanOrderConfig] 读取 Redis 缓存失败:deviceId={}", deviceId, e);
|
||||
}
|
||||
|
||||
// 2. 从数据库查询
|
||||
OpsAreaDeviceRelationDO relation = relationMapper.selectByDeviceId(deviceId);
|
||||
// 2. 缓存未命中,调用 Ops 模块
|
||||
CommonResult<DeviceRelationDTO> result = areaDeviceApi.getDeviceRelationWithConfig(deviceId);
|
||||
|
||||
if (relation == null || !relation.getEnabled()) {
|
||||
// 缓存空值,防止缓存穿透(TTL 较短:60秒)
|
||||
stringRedisTemplate.opsForValue().set(cacheKey, NULL_CACHE_VALUE, 60, TimeUnit.SECONDS);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
log.warn("[CleanOrderConfig] 调用 Ops 模块获取设备配置失败:deviceId={}", deviceId);
|
||||
return null;
|
||||
}
|
||||
|
||||
AreaDeviceConfigWrapper wrapper = wrapConfig(relation);
|
||||
DeviceRelationDTO dto = result.getData();
|
||||
|
||||
// 3. 写入缓存
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
cacheKey,
|
||||
JsonUtils.toJsonString(wrapper),
|
||||
CONFIG_CACHE_TTL_SECONDS,
|
||||
TimeUnit.SECONDS
|
||||
);
|
||||
// 检查是否启用
|
||||
if (dto.getEnabled() == null || !dto.getEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
return wrapDto(dto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCache(Long deviceId) {
|
||||
// 清除设备配置包装器缓存
|
||||
stringRedisTemplate.delete(formatWrapperKey(deviceId));
|
||||
log.info("[CleanOrderConfig] 清除设备配置缓存:deviceId={}", deviceId);
|
||||
// 缓存已由 Ops 模块管理,无需操作
|
||||
log.debug("[CleanOrderConfig] evictCache 由 Ops 模块管理:deviceId={}", deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictAreaCache(Long areaId) {
|
||||
// 清除区域+类型配置缓存
|
||||
stringRedisTemplate.delete(formatAreaTypeKey(areaId, "TRAFFIC_COUNTER"));
|
||||
stringRedisTemplate.delete(formatAreaTypeKey(areaId, "BEACON"));
|
||||
stringRedisTemplate.delete(formatAreaTypeKey(areaId, "BADGE"));
|
||||
log.info("[CleanOrderConfig] 清除区域配置缓存:areaId={}", areaId);
|
||||
// 缓存已由 Ops 模块管理,无需操作
|
||||
log.debug("[CleanOrderConfig] evictAreaCache 由 Ops 模块管理:areaId={}", areaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装配置数据
|
||||
*/
|
||||
private AreaDeviceConfigWrapper wrapConfig(OpsAreaDeviceRelationDO relation) {
|
||||
private AreaDeviceConfigWrapper wrapDto(AreaDeviceDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
return new AreaDeviceConfigWrapper(
|
||||
relation.getDeviceId(),
|
||||
relation.getDeviceKey(),
|
||||
relation.getProductId(),
|
||||
relation.getProductKey(),
|
||||
relation.getAreaId(),
|
||||
relation.getRelationType(),
|
||||
relation.getConfigData()
|
||||
dto.getDeviceId(),
|
||||
dto.getDeviceKey(),
|
||||
dto.getProductId(),
|
||||
dto.getProductKey(),
|
||||
dto.getAreaId(),
|
||||
dto.getRelationType(),
|
||||
convertConfig(dto.getConfigData())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化设备配置包装器缓存 Key
|
||||
* 包装配置数据
|
||||
*/
|
||||
private static String formatWrapperKey(Long deviceId) {
|
||||
return String.format(CONFIG_WRAPPER_KEY_PATTERN, deviceId);
|
||||
private AreaDeviceConfigWrapper wrapDto(DeviceRelationDTO dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
return new AreaDeviceConfigWrapper(
|
||||
dto.getDeviceId(),
|
||||
dto.getDeviceKey(),
|
||||
dto.getProductId(),
|
||||
dto.getProductKey(),
|
||||
dto.getAreaId(),
|
||||
dto.getRelationType(),
|
||||
convertConfig(dto.getConfigData())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化区域+类型配置缓存 Key
|
||||
* 转换配置数据 Map -> CleanOrderIntegrationConfig
|
||||
*/
|
||||
private static String formatAreaTypeKey(Long areaId, String relationType) {
|
||||
return String.format(CONFIG_AREA_TYPE_KEY_PATTERN, areaId, relationType);
|
||||
private CleanOrderIntegrationConfig convertConfig(java.util.Map<String, Object> configData) {
|
||||
if (configData == null || configData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return JsonUtils.parseObject(JsonUtils.toJsonString(configData), CleanOrderIntegrationConfig.class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user