feat(iot): 新增设备类型字段和分组管理功能

- 在 iot_things_model 表添加 dev_type 设备类型字段
- 修改 model_name 字段字符集并更新注释
- 新增设备类型字典数据和翻译配置
- 创建 iot_group 设备分组表并初始化基础数据
- 在 iot_category 表添加 industry_code 设备编码字段
- 更新 ThingsModel 实体类添加 devType 属性
- 修改 Excel 导入功能支持 sheetName 参数并优化类型识别逻辑
- 更新数据库映射文件支持设备类型字段操作
- 优化缓存实现按设备类型区分标识符避免冲突
- 调整导入数据验证逻辑并完善错误处理机制

Signed-off-by: Gjm <你的邮箱>
This commit is contained in:
Gjm
2026-04-09 16:38:57 +08:00
parent 7c07066408
commit a65b23cdad
8 changed files with 240 additions and 21 deletions

View File

@@ -269,7 +269,8 @@ public class TSLCacheImpl implements ITSLCache {
}
}
//List -> MAP
Map<String, String> thingsModelMap = thingsModels.stream().collect(Collectors.toMap(ThingsModel::getIdentifier,
Map<String, String> thingsModelMap = thingsModels.stream().collect(Collectors.toMap(
thingsModel -> thingsModel.getIdentifier() + "_" + (thingsModel.getDevType() != null ? thingsModel.getDevType() : 0),
thingsModel -> {
//转换数据,减少不必要数据
ThingsModelValueItem dto = new ThingsModelValueItem();
@@ -281,9 +282,11 @@ public class TSLCacheImpl implements ITSLCache {
.setName_en_US(thingsModel.getModelName_en_US())
.setOrder(thingsModel.getModelOrder())
.setModelId(thingsModel.getModelId())
.setConfig(thingsModel.getModbusConfig());
.setConfig(thingsModel.getModbusConfig())
.setDevType(thingsModel.getDevType());
return JSONObject.toJSONString(dto);
}));
},
(existingValue, newValue) -> existingValue));
/*缓存到redis*/
String cacheKey = RedisKeyBuilder.buildTSLCacheKey(productId);

View File

@@ -30,7 +30,7 @@ public class ThingsModel extends BaseEntity
/** 物模型名称 */
@ApiModelProperty("物模型名称")
@Excel(name = "物模型名称" ,prompt = "必填")
@Excel(name = "Description" ,prompt = "必填")
private String modelName;
@@ -60,7 +60,7 @@ public class ThingsModel extends BaseEntity
/** 标识符,产品下唯一 */
@ApiModelProperty("标识符,产品下唯一")
@Excel(name = "标识符",prompt = "modbus不填默认为寄存器地址")
@Excel(name = "MeasuringPointName",prompt = "modbus不填默认为寄存器地址")
private String identifier;
/** 模型类别1-属性2-功能3-事件) */
@@ -111,7 +111,7 @@ public class ThingsModel extends BaseEntity
/** 数据类型integer、decimal、string、bool、array、enum */
@ApiModelProperty(value = "数据类型", notes = "integer、decimal、string、bool、array、enum")
@Excel(name = "数据类型", prompt = "integer、decimal、string、bool、array、enum")
@Excel(name = "DataType", prompt = "integer、decimal、string、bool、array、enum")
private String datatype;
@Excel(name = "有效值范围")
@@ -131,5 +131,7 @@ public class ThingsModel extends BaseEntity
private String language;
private List<String> modelIdList;
/** 设备类型 */
private Integer devType;
}

View File

@@ -106,4 +106,6 @@ public class ThingsModelValueItem {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date ts;
private Integer devType;
}

View File

@@ -108,7 +108,7 @@ public interface IThingsModelService
* @param tempSlaveId 从机编码
* @return 结果
*/
public String importData(List<ThingsModel> lists,Long productId);
public String importData(List<ThingsModel> lists,Long productId,String sheetName);
/**
* 根据产品id删除 产品物模型以及物模型缓存

View File

@@ -4,6 +4,7 @@ import java.util.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import iot.lidee.common.core.domain.entity.SysDictData;
import iot.lidee.common.core.domain.entity.SysUser;
import iot.lidee.common.core.redis.RedisCache;
import iot.lidee.common.core.redis.RedisKeyBuilder;
@@ -29,6 +30,7 @@ import iot.lidee.iot.model.modbus.ModbusAndThingsVO;
import iot.lidee.iot.model.varTemp.EnumClass;
import iot.lidee.iot.service.IThingsModelService;
import iot.lidee.iot.cache.ITSLValueCache;
import iot.lidee.system.mapper.SysDictDataMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -69,6 +71,8 @@ public class ThingsModelServiceImpl implements IThingsModelService {
private ITSLValueCache thingModelCache;
@Resource
private ITSLCache itslCache;
@Resource
private SysDictDataMapper sysDictDataMapper;
/**
@@ -130,6 +134,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
ThingsModel input = new ThingsModel();
input.setProductId(thingsModel.getProductId());
input.setLanguage(SecurityUtils.getLanguage());
input.setDevType(thingsModel.getDevType());
List<ThingsModel> list = thingsModelMapper.selectThingsModelList(input);
Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
if (!isRepeat) {
@@ -258,6 +263,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
ThingsModel input = new ThingsModel();
input.setProductId(thingsModel.getProductId());
input.setLanguage(SecurityUtils.getLanguage());
input.setDatatype(thingsModel.getDatatype());
List<ThingsModel> list = thingsModelMapper.selectThingsModelList(input);
Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()) && x.getModelId().longValue() != thingsModel.getModelId());
if (!isRepeat) {
@@ -359,7 +365,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
* @param lists 数据列表
* @return 结果
*/
public String importData(List<ThingsModel> lists, Long productId) {
public String importData(List<ThingsModel> lists, Long productId,String sheetName) {
if (null == productId || CollectionUtils.isEmpty(lists)) {
throw new ServiceException("导入数据异常");
}
@@ -369,13 +375,33 @@ public class ThingsModelServiceImpl implements IThingsModelService {
StringBuilder failSb = new StringBuilder();
boolean result = false;
Product product = productMapper.selectProductByProductId(productId);
SysDictData sysDictData = new SysDictData();
sysDictData.setDictType("model_dev_type");
List<SysDictData> dictData = sysDictDataMapper.selectDictDataList(sysDictData);
Integer sheetNo = null;
if (dictData != null && StringUtils.isNotEmpty(sheetName)) {
for (SysDictData data : dictData) {
String dictLabel = data.getDictLabel();
log.info("对比字典项 - Label: [{}], Value: [{}]", dictLabel, data.getDictValue());
if (StringUtils.isNotEmpty(dictLabel) && dictLabel.trim().equals(sheetName.trim())) {
sheetNo = Integer.valueOf(data.getDictValue());
log.info("匹配成功! sheetNo = {}", sheetNo);
break;
}
}
}
if (sheetNo == null) {
log.warn("未找到Sheet名称 [{}] 对应的字典配置sheetNo将为null", sheetName);
}
for (ThingsModel model : lists) {
try {
model.setProductId(product.getProductId());
model.setProductName(product.getProductName());
//处理数据定义
this.parseSpecs(model);
result = this.importData(model);
result = this.importData(model,sheetNo);
success++;
succSb.append("<br/>").append(success).append(",采集点: ").append(model.getModelName());
} catch (Exception e) {
@@ -384,12 +410,12 @@ public class ThingsModelServiceImpl implements IThingsModelService {
failSb.append("<br/>").append(failure).append(",采集点: ").append(model.getModelName()).append("导入失败");
}
}
if (result) {
log.error("标识符不能重复");
failure++;
failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
throw new ServiceException(failSb.toString());
}
// if (result) {
// log.error("标识符不能重复");
// failure++;
// failSb.append("<br/>").append(failure).append(",采集点: ").append("标识符不能重复");
// throw new ServiceException(failSb.toString());
// }
if (failure > 0) {
throw new ServiceException(failSb.toString());
}
@@ -404,10 +430,11 @@ public class ThingsModelServiceImpl implements IThingsModelService {
/**
* 导入单个物模型
*/
private boolean importData(ThingsModel thingsModel){
private boolean importData(ThingsModel thingsModel,Integer sheetNo){
ThingsModel input=new ThingsModel();
input.setProductId(thingsModel.getProductId());
input.setLanguage(SecurityUtils.getLanguage());
input.setDevType(sheetNo);
List<ThingsModel> list = thingsModelMapper.selectThingsModelList(input);
Boolean isRepeat = list.stream().anyMatch(x -> x.getIdentifier().equals(thingsModel.getIdentifier()));
if (!isRepeat) {
@@ -419,6 +446,7 @@ public class ThingsModelServiceImpl implements IThingsModelService {
thingsModel.setTenantId(user.getUserId());
thingsModel.setTenantName(user.getUserName());
}
thingsModel.setDevType(sheetNo);
thingsModel.setCreateTime(DateUtils.getNowDate());
thingsModelMapper.insertThingsModel(thingsModel);
return false;

View File

@@ -30,6 +30,7 @@
<result property="remark" column="remark"/>
<result property="isReadonly" column="is_readonly"/>
<result property="modelOrder" column="model_order"/>
<result property="devType" column="dev_type"/>
</resultMap>
<resultMap type="iot.lidee.iot.model.ThingsModelPerm" id="ThingsModelPermResult">
@@ -70,7 +71,8 @@
update_time,
remark,
is_readonly,
model_order
model_order,
dev_type
from iot_things_model
</sql>
@@ -84,13 +86,16 @@
m.model_name as model_name_zh_cn, t.en_us as model_name_en_us,
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order,m.dev_type
from iot_things_model m
left join iot_things_model_translate t on m.model_id = t.id
<where>
<if test="productId != null">
and m.product_id = #{productId}
</if>
<if test="modelName != null and modelName != ''">
and model_name like concat('%',#{modelName},'%')
</if>
<if test="type!=null">
and type = #{type}
</if>
@@ -106,6 +111,9 @@
<if test="isReadonly != null ">
and is_readonly = #{isReadonly}
</if>
<if test="devType != null ">
and dev_type = #{devType}
</if>
<if test="modelIdList != null ">
model_id in
<foreach collection="modelIdList" item="item" separator="," open="(" close=")">
@@ -157,7 +165,7 @@
end as model_name,
m.product_id, m.product_name, m.tenant_id, m.tenant_name, m.identifier, m.type, m.datatype, m.formula,
m.specs, m.is_chart, m.is_share_perm, m.is_history, m.is_monitor, m.is_app, m.del_flag, m.create_by,
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order
m.create_time, m.update_by, m.update_time, m.remark, m.is_readonly, m.model_order,m.dev_type
from iot_things_model m
left join iot_things_model_translate t on m.model_id = t.id
where model_id = #{modelId}
@@ -258,6 +266,9 @@
<if test="modelOrder != null">
model_order,
</if>
<if test="devType != null">
dev_type,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="modelName != null and modelName != ''">
@@ -329,6 +340,9 @@
<if test="modelOrder != null">
#{modelOrder},
</if>
<if test="devType != null">
#{devType},
</if>
</trim>
</insert>
@@ -438,6 +452,9 @@
<if test="modelOrder != null">
model_order = #{modelOrder},
</if>
<if test="devType != null">
dev_type = #{devType},
</if>
</trim>
where model_id = #{modelId}
</update>